JavaScript的诞生
- JavaScript诞生于1995年,当时,它的主要目的是处理以前由服务器端需要负责的一些没有输入验证操作。在JavaScript问世之前,必须把表单数据发送到服务器端才能确定用户是否没有填写某个必填域,是否输入了无效的值。Netscape Navigator希望通过JavaScript来解决这个问题。在人们普遍使用电话拨号上网的时代,能够在客户端完成一些基本的验证任务绝对是令人兴奋的。毕竟,拨号上网速度之慢,导致了与服务器的每一次数据交换事实上都成了对人们耐心的一次考验。由此诞生了JavaScript!
JavaScript在1995年2月一经发布就获得了巨大的成功,Netscape随后在Netscape Navigator 3(网景浏览器)中发布了JavaScript 1.1版本。JavaScript发展至今不断迭代,已经取得了伟大成就;但是JavaScript是由公司开发而成的,不便于其他公司拓展和使用,同时JavaScript开发者也在丰富自己的代码和选择使用框架方式,让JavaScript运行在标准兼容的、无障碍的网页中。
2、 JavaScript语言版本更迭
- 为了让JavaScript能够运行在标准兼容的、无障碍的网页中,也就是需要解决JavaScript的兼容问题;因此,欧洲计算机制造商协会ECMA牵头制定JavaScript标准,取名为ECMAScript,至此,JavaScript 的核心语言ECMAScript产生。ECMAScript 是一门由 ECMA TC39 委员会标准化的编程语言,其规定了JavaScript的编程语法和基础核心知识,是所有浏览器厂商共同遵守的一套JavaScript语法工业标准。其在1997年正式发布第一版,历经十数年的迭代更新,于2022年发布了ECMAScript 13,其功能空前强大,增加了更加丰富的新特性。
3、JavaScript的书写位置
- 和css一样 我们的js也可以有很多种方式写在页面上让其生效
- js也有多种方式书写分为 行内式、内嵌式、外链式
3-1、行内式(不推荐)
- 写在标签上的js代码需要依靠事件来触发
1
2
3
4// 写在a标签的href属性上
<a href="javascript:alert('我是一个弹出层')">点击一下试试</a>
// 写在其他元素上
<div onclick="alert('我是一个弹出层')">点击一下试试</div>
3-2、内嵌式
- 内嵌式的js代码会在页面打开的时候直接触发
1
2
3
4
5// 在html页面属性一个script标签 标签内部书写js代码
<script type="text/javascript">
alert("我是一个弹出层")
</script>
// 注:script标签可以放在head里面也可以放到body里面
3-2、外链式
- 外链式js代码只要引入html页面 就会在页面打开的时候直接触发
- 新建一个.js后缀的文件 在文件内书写js代码 把写好的js文件引入html页面
1
2// 我是index.js文件
alert('我是一个弹出层')
1 | <!-- 我是一个html文件 --> |
4、变量(重点)
- 变量指的是在程序中保存数据的一个容器
- 变量是计算机内存中存储数据的标识符 根据变量名称可以获取打内存中存储的数据
- 也就是说 我们向内存中存储了一个数据 然后要给这个数据起一个名字 为了是我们以后再次找到他
1 | // 定义一个变量 |
- 注意
1、一个变量名只能存储一个值
2、当再次给一个变量赋值的时候 前面一次的值就没有了
3、变量名称区分大小写(JS 严格区分大小写)
4-1 变量的命名规则和命名规范
- 规则:必须遵守的 不遵守就是错
1、一个变量名称可以由 数字、字母、英文下划线、美元符号($) 组成
2、严格区分大小写
3、不能由数字开头
4、不能是保留字或者关键字
5、不要出现空格 - 规范:建议遵守的(开发者默认) 不遵守不会报错
1、变量名尽量有意义(语义化)
2、遵循驼峰命名规则 由多个单词组成的时候 从第二个单词开始首字母大写
3、不要使用中文
5、数据类型(重点)
- 是指我们存储在内存中的数据的类型
- 我们通常分为两大类 基本数据类型和复杂数据类型
- 除了上面的这些,ES6新增了一些数据类型包括:
- Symbol 类型(基本)
- Set 类型(复杂)
- Map 类型(复杂)
- WeakSet 类型(复杂)
- WeakMap 类型(复杂)
- TypedArray 类型(复杂)
5-1、判断数据类型
- 使用typeOf关键字来进行判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var a = "aaaa"
console.log(typeof a) //string
var b = 111
console.log(typeof b) //number
var c = true
console.log(typeof c) //boolean
var d = undefined
console.log(typeof d) //undefined
var e = null
console.log(typeof e) //object 检测null是为对象类型
var f = {a:1}
console.log(typeof f) //object 检测对象是为对象类型
var g = []
console.log(typeof g) //object 检测数组是为对象类型
var x = []
console.log(typeof typeof x) // string 因为第一次typeof检测之后会返回字符串 所以第二次检测都会是字符串
5-2、其他数据类型转成数字
- 1、Number(变量)
可以把一个变量强制转换成数值类型
可以转换小数 会保留小数
可以转换布尔值
遇到不可转换的都会返回NaN - 2、parseInt(变量)
从第一位开始检查 是数字就转换 直到一个不是数字的内容
开头就不是数字 那么直接返回NaN
不认识小数点 只能保留整数 - 3、parseFloat(变量)
从第一位开始检查 是数字就转换 直到一个不是数字的内容
开头就不是数字 那么直接返回NaN
认识小数点 - 4、除了加法以外的数字运算
运算符两边都是可计算数字才行
如果运算符任何一边不是可运算数字 那么返回NaN
加法不可用
5-3、其他数据类型转成字符串
- 1、变量.toString()
有一些数据类型不能使用toString()方法 比如undefined和null - 2、String(变量)
所有数据类型都可以 - 3、使用加法运算
在JS里面 +有两个含义
字符串拼接:只要+任意一边是字符串 就会进行字符串拼接
加法运算:只要+两边都是数字的时候 才会进行数学计算
5-4、其他数据类型转成布尔
- 1、Boolean(变量)
在js中 只有’’、0、null、undefined、NaN、这些都是false
其余都是true
5-5、数据类型相关问题
5-6、数据变量与内存
数据
内存
变量及内存、变量、数据关系
6、运算符
- 就是在代码里面进行运算的时候使用的符号 不光只是数字运算 我们在js里面还有很多的运算方式
6-1、数字运算符
1、+
只要符号两边都是数字的时候才会进行加法运算
只要符号任意一边是字符串类型 就会进行字符串拼接
2、-
会执行减法运算
会自动把两边都转换成数字进行运算
3、*
会执行乘法运算
会自动把两边都转换成数字进行运算
4、/
会执行除法运算
会自动把两边都转换成数字进行运算
5、%
会执行取余运算
会自动把两边都转换成数字进行运算
6-2、赋值运算符
1、=
1 | 就是把=右边的赋值给等号左边的变量名 |
2、+=
1 | var a = 10 |
3、-=
1 | var a = 10 |
4、*=
1 | var a = 10 |
5、/=
1 | var a = 10 |
6、%=
1 | var a = 10 |
6-3、比较运算符
1、==
比较符号两边的值是否相等 不管数据类型
1==’1’
两个值是一样的 所以得到true2、===
比较符号两边的值和数据类型是否相等
1===’1’
两个值虽然一样 但是因为数据类型不一样 所以得到false3、!=
比较符号两边的值是否不等
1!=’1’
因为两边的值是相等的 所以比较他们不等的时候得到false4、!==
比较符号两边的数据类型和值是否不等
1!==’1’
因为两边的数据类型确实不一样 所以的到true5、>=
比较左边的值是否大于或等于右边的值
1>=1 结果是true
1>=0 结果是true
1>=2 结果是false6、<=
比较左边的值是否小于或等于右边的值
1<=2 结果是true
1<=1 结果是true
1<=0 结果是false7、>
比较左边的值是否大于右边的值
1>0 结果是true
1>1 结果是false
1>2 结果是false8、>
比较左边的值是否小于右边的值
1<2 结果是true
1<1 结果是false
1<0 结果是false
6-4、逻辑运算符
- 1、&&
进行 且 的运算
符号左边必须为true 并且右边也是true 才会返回true
只要一边不是true那么就会返回false
true && true 结果是true
true && false 结果是false
false && true 结果是false
false && false 结果是false - 2、||
进行 或 的运算
符号左边必须为true 并且右边也是true 才会返回true
只有两边都是false那么就会返回false
true || true 结果是true
true || false 结果是true
false || true 结果是true
false || false 结果是false - 3、!
进行 取反 的运算
本身是true 会变成false
本身是false 会变成true
!true 结果是false
!false 结果是true - 4、特殊情况
① !!x =>转换成布尔值② && 短路用法1
2var x="1111"
console.log(!!x) //true③ || 短路用法1
2
3
4
5
6var y
console.log(y.toString()) //会报错 并且不会再向下执行 因为y的值是undefined 所以没有.toString()这个方法
console.log(1) // 不会执行
//短路用法
console.log(y && y.toString()) // 不会报错 打印undefined 因为y没值 被视为undefined undefined为false 所以不会再执行&& 右边的语句
console.log(1) // 正常执行1
2var z=""
console.log(z ||"这个家伙很懒,什么也没有留下") // z的值空字符串 为假 继续向下执行 打印"这个家伙很懒,什么也没有留下"
6-5、自增自减运算符
1、++
进行自增运算
分成两种 前置++和后置++
前置++ 会先把值自动+1 再返回
1
2
3var a = 10
console.log(++a)
// 会返回11 并且把a的值变成11
后置++ 会先把值返回 在自动+1
1
2
3var a = 10
console.log(a++)
// 会返回10 并且把a的值变成112、——
进行自减运算
分成两种 前置——和后置——
和++运算符道理一样3、注意点
1
2
3
4
5
6
7
8
9
10
11var number=10
// number++
// document.write(number) // 页面展示11
// ++number
// document.write(number) // 页面展示11
// document.write(++number) // 页面展示11
document.write(number++) // 页面展示10
console.log(number) // 打印114、小案例
1
2
3var n=10
var m= ++n + n++ + ++n // 第一个n是11 第二个n表面是11 其实已经值已经是12 第三n是13
console.log(m) // 35
6-6、三元运算符
三元运算 就是用两个符号组成一个语句
语法:条件 ? 条件为true的时候执行 : 条件为false的时候执行
1
2var age=18
age>=18?alert('已经成年了'):alert('没有成年')小案例
1
2
3
4// 满200减10 满100减5
var sum=190
var newSum=sum>200?sum-10:(sum>100?sum-5:sum)
console.log(newSum) // 185
7、分支结构
- 我们的js代码都是顺序执行的(从上到下)
- 逻辑分支就是根据我们设定好的条件来决定要不要执行某些代码
7-1、IF条件分支结构(重点)
if语句
- 通过一个if语句来决定代码是否执行
- 语法if(条件){要执行的代码}
- 通过()里面的条件是否成立来决定{}里面的代码是否执行
1
2
3
4
5
6
7
8// 条件为true的时候执行 {} 里面的代码
if(true){
alert("因为是条件true 我会执行")
}
// 条件为false的时候不执行 {} 里面的代码
if(false){
alert("因为是条件false 不会执行")
}
if else语句
- 通过if条件来决定 执行哪一个{}里面的代码
- 语法:if(条件){条件为true的时候执行}else{条件为false的时候执行}
- 两个{}内的代码一定有一个会执行
1
2
3
4
5
6
7
8
9
10
11
12// 条件为true的时候 会执行if后面的{}
if(true){
alert("因为是条件true 我会执行")
}else{
alert("因为条件是true 我不会执行")
}
// 条件为false的时候 会执行else后面的{}
if(false){
alert("因为是条件false 不会执行")
}else{
alert("因为条件为false 我会执行")
}
if else if…语句
- 可以通过if和else if来设置多个条件进行判断
- 语法:if(条件1){条件1为 true 的时候执行}else if(条件2){条件2为true的时候执行}
- 会从头开始依次判断条件
如果第一个条件为true 那么就是执行后面{}里面的内容
如果第一个条件为false 那么就会判断第二个条件 依次类推 - 多个{} 只会有一个被执行 一旦有一个条件为true了 后面的就不在判断了
1
2
3
4
5
6
7
8
9
10
11var sum = 190
var newSum
if(sum>200){
newSum = sum-10
}else if(sum>100){
newSum = sum-5
}else if(sum>50){
newSum = sum-3
}else if(sum<50){
newSum = sum
}
if else if…else语句
- 和指向的if else if… 基本一致 只不过是在所有条件都不满足的时候 执行最后else后面的{}
1
2
3
4
5
6
7
8
9
10// 第一个条件为false 第二个条件为false 最终会打印"我是代码3"
// 只有前面所有的条件都不满足的时候会执行else后面的{}里面的代码
// 只要前面有一个条件满足了 那么后面的就都不会执行了
if(false){
alert("我是代码1")
}else if(false){
alert("我是代码2")
}else{
alert("我是代码3")
}
if的小案例
1 | /* |
7-2、switch条件分支结构(重点)
- 也是条件判断语句的一种
- 是对于某一个变量的判断
- 注意
1、===
2、break必须写
3、不要比较运算符 - 语法
1
2
3
4
5
6
7
8
9
10
11
12
13switch(要判断的变量){
case 情况1:
情况1要执行的代码
break
case 情况2:
情况2要执行的代码啊
break
case 情况3:
情况3要执行的代码啊
break
default:
上述情况不满足的时候执行的代码
} - 小案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/*
订单状态码
1、未付款
2、已付款
3、已发货
4、已完成
*/
var code = 1
switch(code){
case 1:
document.write("未付款")
break
case 2:
document.write("已付款")
break
case 3:
document.write("已发货")
break
case 4:
document.write("已完成")
break
default:
document.write("出错了")
} - 小案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26/*
根据1~12的数字来输出一个月有几天
*/
var month = 1
switch(month){
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
console.log("31天")
break
case 2:
document.write("28天")
break
case 4:
case 6:
case 9:
case 11:
document.write("30天")
break
default:
document.write("出错了")
}
8、循环结构(重点)
- 循环结构 就根据某些给出的条件 重复的执行同一段代码
- 循环必须要有某些固定的内容组成
1、初始化
2、条件判断
3、要执行的代码
4、自身改变
8-1、while循环
- while 中文叫 当…时 其实就是当条件满足时就执行代码 一旦不满足就不执行
- 语法 while(条件){满足条件就执行}
- 因为满足条件就执行 所以我们写的时候一定要注意 就是设定一个边界值 不然就一直循环下去了
1
2
3
4
5
6
7
8
9
10// 1、初始化条件
var num = 0
// 2、条件判断
while(num < 10){
// 3、要执行的代码
console.log(num)
// 4、自身改变 放在最后
num = num + 1
// 如自身没有改变 将无限循环
} - 小案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 案例1:求1~100所有数字的和
var n = 1
var sum = 0
while(n <= 100){
sum+=n
n++
}
// 案例2:求一个数字的阶乘
var m = 5
var sum = 1
while(n>0){
sum*=m
n--
}
8-2、Do while循环
- 是一个和while循环类似的循环
- while会先进行条件判断 满足就执行 不满足直接就不执行
- 但是do while循环是 先不管条件 先执行一回 然后再开始进行条件判断
- 语句:do{要执行的代码} while(条件)
1
2
3
4
5
6// 下面这个代码 条件一开始不满足 但是依旧会执行一次do后面{}内部的代码
var num = 10
do{
console.log("我执行了一次")
num = num + 1
}while(num<10)
8-3、for循环
- 和while和do while循环都不太一样的一种循环结构
- 道理是和其他两种一样的 都是循环执行代码的
- 语法:for(var i=0;i<10;i++){要执行的代码}
1
2
3
4
5
6// 把初始化 条件判断 自身改变 写在了一起
for(var i=0;i<=10;i++){
// 这里写的是要执行的代码
console.log(i)
}
// 控制台会依次输出1~10 - 案例
1
2
3
4
5
6
7
8
9
10
11
12
13// 1~100的和
var sum = 0
for(var i=0;i<=100;i++){
sum+=i
}
console.log(sum)
// 2、1000~2000闰年
for(var i=1000;i<=2000;i++){
if(i%400==0 || i%4==0 && i%100!=0){
document.write(n)
}
}
8-4、break终止循环
- 在循环没有进行完毕的时候 因为我设置的条件满足 提前终止循环
- 比如:我要吃五个包子 吃到第三个的时候不能在吃了 我就停止吃包子这个事情
- 要终止循环 就可以直接使用break关键字
1
2
3
4
5
6
7
8
9for(var i=1;i<=5;i++){
// 每循环一次 吃一个包子
console.log("我吃了一个包子")
// 当i的值为3的时候 条件为true 执行{}里面的代码终止循环
// 循环就不会继续向下执行了 也就没有3、4了
if(i===3){
break
}
}
8-5、continue结束本次循环
- 在循环中 把循环的本次跳过去 继续执行后续的循环
- 比如:吃五个包子 到第三个的时候 第三个掉地上了 不吃了 跳过第三个 继续吃第四个和第五个
- 跳过本次循环 就可以使用continue关键字
1
2
3
4
5
6
7
8
9
10for(var i=1;i<=5;i++){
// 当i的值为3的时候 执行{}里面的代码
// {}里面有continue 那么本次循环后面的代码就都不执行了
// 自动算作i为3的这次结束了 去继续执行i=4的那次循环
if(i===3){
console.log("这是第三个包子,掉地上了,我不吃了")
continue
}
console.log("我吃了一个包子")
} - 小案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/*
求质数
质数:除了1和自己本身以外 没有其他约数
=>3 5 7 11 13 17 ...
判断一个数字是质数的思路
=> 7 % 2 不为 0
=> 7 % 3 不为 0
=> 7 % 4 不为 0
=> 7 % 5 不为 0
=> 7 % 6 不为 0
*/
var n = 19
var isZhi=true
for(var i=2;i<n;i++){
if(n%i===0){
isZhi=false
}
}
if(isZhi){
console.log("是")
}else{
console.log("不是")
}
8-6、九九乘法表
- 小案例
1
2
3
4
5
6for (var m = 1; m <= 9; m++) {
for (var n = 1; n <= m; n++) {
document.write("<span style='display:inline-block;width:70px'>" + m + "*" + n + "=" + m * n + "</span>")
}
document.write("<br/>")
}
9、函数的概念
- 对于js来说 函数就是把任意一段代码放在一个盒子里面
- 在我想要让这段代码执行的时候 直接执行这个盒子里面的代码就行
- 演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 这个是我们以前写的一段代码
for(var i=0;i<10;i++){
console.log(i)
}
// 函数 这个{}就是那个盒子
function fn(){
// 这个函数我们以前写的代码
for(var i=0;i<10;i++){
console.log(i)
}
}
// 调用函数
fn() - 定义函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16/*
Function
函数 是一个复杂数据类型
*/
//1、定义函数
// (1) 声明式
function test1(){
console.log("我是函数1")
}
// (2) 赋值式
var test2=function(){
console.log("我是函数2")
}
// 2、调用函数
test1()
test2()
什么是函数
9-1、声明式
- 使用function这个关键字来声明一个函数
- 语法
1
2
3
4
5
6
7function fn(){
// 一段代码
}
// function 声明函数的关键字 表示接下来是一个函数
// fn 函数的名字 我们自己定义的(遵循变量名的命名规则和命名规范)
// () 必须写 是用来放参数的位置
// {} 就是我们用来放一段代码的位置
9-2、赋值式
其实就是和我们使用var关键字一个道理
首先使用var定义一个变量 把一个函数当作值直接赋值给这个变量就可以了
语法
1
2
3
4var fn=function(){
// 一段代码
}
// 不需要在function后面写函数的名字 因为在前面已经有了声明式和赋值式的区别
声明式函数可以先调用再定义
赋值式函数只能先定义再调用
9-3、调用一个函数
- 函数调用就是直接写函数名() 就可以了
- 函数调用阶段
- 就是让盒子里面的代码执行一下
- 让函数执行
- 两种定义函数的方式不同 但调用函数的方式都是一样的
1
2
3
4
5
6
7
8
9
10
11
12
13// 声明式函数
function fn1(){
console.log("我是函数1")
}
// 调用函数
fn1()
// 赋值式函数
var fn2=function(){
console.log("我是函数2")
}
// 调用函数
fn2()
9-4、函数的参数
我们在定义函数和调用函数的时候都出现过()
现在我们来说一下这个()的作用
就是用来放参数的位置
参数分为两种 行参和实参
1
2
3
4
5
6
7
8
9
10
11// 声明式
function fn(形参写在这里){
// 一段代码
}
fn(实参写在这里)
// 赋值式
const fn=function(形参写在这里){
// 一段代码
}
fn(实参写在这里)案例
1
2
3
4
5
6
7function fn(a,b){
var yinLiao = a===1?"可乐":"雪碧"
var linShi = b===1?"薯条":"鸡米花"
var zhuShi ="鸡腿堡"
console.log("我是套餐",yinLiao,linShi,zhuShi)// 我是套餐 可乐 鸡米花 鸡腿堡
}
fn(1,2)注意:
1、可以不传参
2、形参只能在函数内部去使用
9-5、函数的返回值
函数调用本身也是一个表达式 表达式就应该有一个值出现
现在的函数执行完毕之后 是不会有结果出现的
1
2
3
4
5
6
7
8// 比如1+2是一个表达式 那么这个表达式的结果就是3
console.log(1+2) // 3
function fn(){
// 执行代码
}
// fn() 也是一个表达式 这个表达式就没有结果出现
console.log(fn()) // undefinedreturn 关键字就是一个给函数执行完毕一个结果
1
2
3
4
5
6function fn(){
// 执行代码
return 100
}
// 此时 fn() 这个表达式执行完毕之后就有结果出现了
console.log(fn()) // 100小案例
1
2
3
4
5function add(x,y,z){
var result=x+y+z;
return result;
}
console.log(add(1,2,3)) // 6注意:
1、return 后面代码无法执行
2、如果不需要返回值 可以不加return
9-6、预解析
JavaScript代码是由浏览器中的JavaScript解析器来执行的。JavaScript解析器在运行JavaScript代码的时候,分为两步:预解析和代码执行。
预解析过程:
JavaScript解析器会在全局环境下查找 var、function关键字,变量只声明不赋值,函数声明不调用。
预解析只发生在当前作用域下预解析也叫做变量、函数提升
变量提升
定义变量的时候,变量的声明会被提升到当前作用域的最上面,变量的赋值会提升。函数提升
JavaScript解析器首先会把当前作用域的函数声明提前到整个作用域的最前面
变量名和函数名相同,优先执行函数执行过程
变量赋值、函数调用、表达式运算等等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 变量
console.log(myName) // undefined
var myName="zs"
console.log(myName) // zs
// 赋值式
myFunc() // 报错
var myFunc =function(){
console.log("myFunc")
}
myFunc() // 不执行
// 声明式
fn() // fn
function fn(){
console.log("fn")
}
fn() //fn在当前的作用域中,JS代码执行之前,浏览器首先会默认的把所有的带var,和function的进行提前的声明或定义
var 和 function 关键字:
对于带var 和 function 关键字的在预解析的时候操作
var -> 在预解析的时候只是提前声明了
function -> 在预解析的时候提前的声明了 + 定义 都完成了。
预解析只发生在当前的作用域下进行解析。
一开始只会对window环境下的进行预解析,只有函数执行的时候才会对函数中的进行解析
9-7、函数重名
- 变量名和函数名相同 调用函数名会报错
1
2
3
4
5var age = 100
function age() {
console.log("age is 100")
}
age() // 报错 原因是函数名和变量名相同
9-8、作用域
- 什么是作用域 就是一个变量可以生效的范围
- 变量不是在所有地方都可以使用的 而这个变量的使用范围就是作用域
全局作用域
- 全局作用域是最大的作用域
- 在全局作用域中定义的变量可以在任何地方使用
- 页面打开的时候 浏览器会自动给我们生成一个全局作用域window
- 这个作用域会一直存在 直到页面关闭销毁
1
2
3// 下面两个变量都是存在在全局作用域下面的 都是可以在任意地方使用的
var num = 100
var num1 = 200
局部作用域
- 局部作用域就是在全局作用域下面开辟出来的一个相对小一些的作用域
- 在局部作用域中定义的变量只能在这个局部作用域内部使用
- 在JS中只有函数能生成一个局部作用域 别的都不行
- 每一个函数 都是一个局部作用域
1
2
3
4
5
6
7
8// 这个num是一个全局作用域下的变量 在任何地方都可以使用
var num = 100
function fn(){
// 下面这个变量就是一个fn 局部作用域内部的变量
// 只能在fn函数内部使用
var num2 =200
}
fn()
访问规则
- 当我想获取一个变量的值的时候 我们管这个行为叫做访问
- 获取变量的规则
首先,在自己的作用域内部查找,如果有,就直接拿来使用
如果没有就去上一级作用域查找,如果有,就拿来使用
如果没有,就继续去上一级作用域查找,依次类推
如果一直到全局作用域都没有找到这个变量,那么就会直接报错 - 案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var num = 100
function fn(){
var num2 = 200
function fun(){
var num3 = 300
console.log(num3) // 自己作用域内有,拿来使用
console.log(num2) // 自己作用域没有,就去上一级,也就fn的作用域里面找,发现有,拿来使用
console.log(num) // 自己这没有,去上一级fn那里也没有,再上一级到全局作用域,发现有,直接用
console.log(a) // 自己没有,一级一级找上去到全局作用域都没有,就会报错
}
fun()
}
fn() - 变量的访问规则 也叫做作用域的查找机制
- 作用域的查找机制只能是向上找 不能向下找
赋值规则
- 当你想给一个变量赋值的时候 那么就先要找到这个变量 在给他赋值
- 变量的赋值规则
先在自己作用域内部查找 有就直接赋值
没有就去上一级作用域内部查找 有就直接赋值
还没有再去上一级作用域查找 有就直接赋值
如果一直找到全局作用域都没有 那么就把这个变量定义为全局变量 再给他赋值
1 | function fn(){ |
9-9、自执行函数
- 属于匿名函数,直接调用;
- 在初次加载的时候,会执行一次(自执行函数只能执行一次)
- 自执行函数会形成一个独立的作用域
- 优点:将全局变量写在立即执行函数里,作为局部变量,防止变量污染全局(避免多次声明造成变量覆盖)
- 缺点:不能重复调用
10、对象
- 对象是一个复杂数据类型
- 其实说是复杂 但是没有多复杂 只不过存储了一些基本数据类型的一个集合
1
2
3
4
5var obj={
num:100,
str:"hello world",
boo:true
} - 这里{}和函数中的{}不一样
- 函数里面的是写代码的 而对象里面是写一些数据的
- 对象就是一个键值对的集合
- {} 里面的每一个键都是成员
- 也就是说 我们可以把一些数据放在一个对象里面 那么他们就互不干扰了
- 其实就是我们准备一个房子,把我想要的数据放进去,然后把房子的地址给到变量名,当我们需要某一个数据的时候,就可以根据变量名里面存储的地址找到对应的房子,然后去房子里面找到对应的数据
10-1、创建对象
- 字面量的方式创建对象
1
2
3
4
5// 创建一个对象
var obj={}
// 向对象中添加成员
obj.name = "jack"
obj.age = 18 - 内置构造函数的方式创建对象
1
2
3
4
5// 创建一个空对象
var obj=new Object()
// 向对象中添加成员
obj.name = "Rose"
obj.age = 20 - Object 是js内置给我们的构造函数 用于创建一个对象使用的
10-2、操作对象
1 | // 第一种操作方法 |
10-3、遍历对象
1 | var obj={ |
10-4、不同数据类型的存储
- 即然我们区分了基本数据类型和复杂数据类型
- 那么他们之间就一定会存在一些区别
- 他们最大的区别就是在存储上的区别
- 我们的存储空间分成两种 栈 和 堆
- 栈:主要存储基本数据类型的内容
- 堆:主要存储复杂数据类型的内容
基本数据类型在内存中的存储情况
- var num = 100 在内存中的存储情况
- 直接在 栈空间 内有存储一个数据
复杂数据类型在内存中的存储情况
- 下面这个对象的存储
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 错误赋值方式
var obj={
name:"zs",
age:100
}
var obj2=obj
obj2.name='ls' //修改obj2的属性值的时候 会影响到obj的属性值
console.log(obj,obj2) // obj{name: 'ls', age: 100} obj2{name: 'ls', age: 100}
var obj={
name:"zs",
age:100
}
var obj2={}
for(var i in obj){
obj2[i]=obj[i]
}
obj2.name="ls" // 不会影响obj
console.log(obj,obj2) // obj{name: 'zs', age: 100} obj2{name: 'ls', age: 100} - 复杂数据类型的存储
1、在堆内存里面开辟一个存储空间
2、把数据存储到存储空间内
3、把变量放到栈内存中
4、堆内存会给栈内存一个地址
5、栈内存中的变量可以根据地址访问到堆内存中的数据
11、数组
- 什么是数组?
- 准确的来说数组是一个数据的集合
- 也就是是我们把一些数据放到盒子里面 按照顺序排好
1
[1,2,3,"hello",true,{name:"zs"}]
- 这就是一个数组 存储着一些数据的集合
数据类型分类
- number / string / boolean / undefined / null / object / function / array
- 数组也是数据类型的一种
- 我们简单的把所有数据类型分为两大类 基本数据类型 和 复杂数据类型
- 基本数据类型:number / string / boolean / undefined / null
- 复杂数据类型:object / function / array
11-1、创建一个数组
- 数组就是一个[ ]
- 在[ ]里面存储着各种各样的数据,按照顺序依次排好
字面量创建一个数组
- 直接使用[]的方式创建一个数组
1
2
3
4// 创建一个空数组
var arr1=[]
// 创建一个有内容的数组
var arr2=[1,2,3]
内置构造函数创建数组
- 使用js的内置构造函数Array创建一个数组
1
2
3
4
5
6
7
8// 创建一个空数组
var arr1=new Array() // []
// 创建一个长度为10的数组
var arr2=new Array(10) // [空属性 × 10]
// 创建一个有内容的数组
var arr3=new Array(1,2,3) // [1,2,3]
数组length的使用
1 | // 清空数组 |
11-2、排序
- 排序 就是把一个乱序的数组 通过我们的处理 让他变成一个有序的数组
冒泡排序
- 先遍历数组,让挨着的两个进行比较,如果前一个比后一个大,那么就把两个换一个位置
- 数组遍历一遍以后,那么最后一个数字就是最大的那个了
- 然后进行第二遍的遍历 还是按照之前的规则,第二大的数字就会跑到倒数第二的位置
- 依次类推,最后就会按照顺序把数组排好
1
2
3
4
5
6
7
8
9
10
11var arr = [4, 3, 2, 1]
for (var i = 0; i < arr.length - 1; i++) {
for (var j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
var temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
}
console.log(arr) // [1,2,3,4]
选择排序
- 先假定数组的第0个就是最小的数字的索引
- 然后遍历数组 只要有一个数字比我小 那么替换之前记录的索引
- 直到数组遍历结束后 就能找到最小的那个索引 然后让最小的索引换到第0个的位置
- 再来第二趟遍历 假定第一个是最小的数字索引
- 在遍历一次数组 找到比我小的那个数字的索引
- 遍历结束后换个位置
- 依次类推 也可以把数组排好
1
2
3
4
5
6
7
8
9
10
11
12
13var arr = [4, 3, 2, 1]
for (var m = 0; m < arr.length - 1; m++) {
var minIndex=m
for (var i = m+1; i < arr.length; i++) {
if (arr[i] < arr[minIndex]) {
minIndex = i
}
}
var temp = arr[m]
arr[m]=arr[minIndex]
arr[minIndex] = temp
}
console.log(arr) // [1,2,3,4]
11-3、数组的常用方法
- 数组是一个复杂数据类型 我们在操作他的时候就不能再想基本数据类型一样操作
- 比如我们想改变一个数组
1
2
3
4
5
6
7
8// 创建一个数组
var arr=[1,2,3]
// 我们想把数组变成只有1和2
arr=[1,2]
// 这样肯定不合理 因为这样不是在改变之前的数组
// 相当于新弄了一个数组给到arr这个变量
// 相对于把arr里面存储的地址给换了 也就是把存储空间换掉了 而不是在之前的空间里面修改
// 所以我们就需要借助一些方法在不改变存储空间的情况下 把存储空间里面的数据改变了
数组方法之push
- push是用来在数组的末尾追加一个元素
1
2
3
4
5
6
7var arr=[1,2,3]
// push 追加一个元素在末尾
// 返回值 长度
arr.push(4)
console.log(arr) //[1,2,3,4]
数组方法之pop
- pop是用来在数组的末尾删除一个元素
1
2
3
4
5
6
7var arr=[1,2,3]
// pop 删除尾部元素
// 返回值 删除的元素
arr.pop()
console.log(arr) //[1,2]
数组方法之unshift
- unshift是用来在数组的头部追加一个元素
1
2
3
4
5
6
7var arr=[1,2,3]
// unshift 追加一个元素在头部
// 返回值 长度
arr.unshift(0)
console.log(arr) //[0,1,2,3]
数组方法之shift
- shift是用来在数组的头部追加一个元素
1
2
3
4
5
6
7var arr=[1,2,3]
// shift 删除一个元素在头部
// 返回值 删除的元素
arr.shift()
console.log(arr) //[2,3]
数组方法之splice
- splice() 方法通过移除或者替换已存在的元素或添加新元素就地改变一个数组的内容。
1
2
3
4
5
6var arr=['zs','ls','ww']
// 返回值 删除的元素
arr.splice(1,1,'asd','A')
// arr.splice(从哪个下标开始删除或添加,删除几个,要添加的内容1,,要添加的内容2)
console.log(arr) // ['zs', 'asd', 'A', 'ww']
数组方法之reverse()
- reverse() 反转数组中的元素 返回反转后的数组
1
2
3
4
5var arr=['zs','ls','ww']
arr.reverse()
console.log(arr) // ['ww', 'ls', 'zs']
数组方法之迭代方法sort()
- sort() 对数组的元素进行排序 返回排序后的数组
1
2
3
4
5
6var arr=[11,21,56,7,3]
arr.sort((a,b)=>{
return b-a
})
console.log(arr) // [56, 21, 11, 7, 3]
数组方法之迭代方法map()
- map()方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。。
1
2
3
4
5
6var arr=['aaa','bbb','ccc','ddd']
// arr.map((当前元素,当前元素索引,数组本身)=>{})
var newArr=arr.map((item,index,arr)=>{
return item+"lmh"
})
console.log(newArr) // ['aaalmh', 'bbblmh', 'ccclmh', 'dddlmh']
数组方法之迭代方法filter()
- filter()方法会对满足条件的元素进行过滤 返回值由满足条件的元素组成
1
2
3
4
5
6
7
8var arr=[{name:"aaa",price:100},{name:"bbb",price:200},{name:"ccc",price:300}]
// arr.filter((当前元素,当前元素索引,数组本身)=>{})
var newArr=arr.filter((item,index,arr)=>{
if(item.price>100){
return item
}
})
console.log(newArr) // [{name:"bbb",price:200},{name:"ccc",price:300}]
数组方法之迭代方法every()
- every() 方法遍历数组内的所有元素是否都能满足条件。它返回一个布尔值。
1
2
3
4
5
6var arr=[80,90,92,94]
// arr.every((当前元素,当前元素索引,数组本身)=>{})
var newArr=arr.every((item,index,arr)=>{
return item>=90
})
console.log(newArr) // false
数组方法之迭代方法some()
- some() 方法遍历数组内的所有元素只要有一个满足条件。它返回一个布尔值。
1
2
3
4
5
6var arr=[8,90,9,4]
// arr.some((当前元素,当前元素索引,数组本身)=>{})
var newArr=arr.some((item,index,arr)=>{
return item>=90
})
console.log(newArr) // true
数组方法之迭代方法find()
- find() 方法会对满足条件的元素进行过滤 返回值是满足条件的第一个元素
1
2
3
4
5
6
7
8var arr=[{name:"aaa",price:100},{name:"bbb",price:200},{name:"ccc",price:300}]
// arr.find((当前元素,当前元素索引,数组本身)=>{})
var newArr=arr.find((item,index,arr)=>{
if(item.price>100){
return item
}
})
console.log(newArr) // {name: 'bbb', price: 200}
数组方法之迭代方法reduce()
- reduce() 方法会逐个遍历数组元素,每一步都将当前元素的值与前一步的结果相加(该结果是之前所有步骤结果的总和)——直到没有更多需要相加的元素。
1
2
3
4
5
6var arr=[{price:100},{price:200},{price:300},{price:400}]
// arr.reduce((上一次的结果,当前元素,当前元素的索引,数组本身)=>{},初始值)
var newArr=arr.reduce((prev,item,index,arr)=>{
return prev+item.price
},0)
console.log(newArr) // 1000
不影响原数组的数组方法之concat()
- concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
1
2
3
4
5
6var arr1=[1,2,3,4]
var arr2=[5,6,7,8]
var arr3=[9,10]
var arr4=arr1.concat(arr2,arr3)
console.log(arr4) // [1,2,3,4,5,6,7,8,9,10]
不影响原数组的数组方法之join()
- join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串,用逗号或指定的分隔符字符串分隔。如果数组只有一个元素,那么将返回该元素而不使用分隔符。
1
2
3
4var arr=[1,2,3,4,5]
var str=arr.join('|');
console.log(str) // 1|2|3|4|5
不影响原数组的数组方法之slice()
- slice() 方法用于截取数组 返回一个新的数组对象
1
2
3
4
5
6
7
8
9
10
11// slice截取(开始索引,结束索引)
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
var arr1=animals.slice(2) //从索引2开始截取
console.log(arr1) //['camel', 'duck', 'elephant']
var arr2=animals.slice(0,2) //从索引0开始截取,到索引2结束 但是不包括索引2的元素
console.log(arr2) //['ant', 'bison']
var arr3=animals.slice() //不传参 就相当于对浅拷贝一份数组 修改这个拷贝出来的数组 不会对原数组造成影响
console.log(arr3) //['ant', 'bison', 'camel', 'duck', 'elephant']
不影响原数组的数组方法之indexOf()
- indexOf() 方法返回数组中第一次出现给定元素的下标,如果不存在则返回 -1。
1
2
3var arr=['aaa','bbb','ccc','ddd','eee']
var res=arr.indexOf('aaa')
console.log(res) // 0
不影响原数组的数组方法之lastIndexOf()
- lastIndexOf() 方法返回数组中给定元素最后一次出现的索引,如果不存在则返回 -1。该方法从 fromIndex 开始向前搜索数组。
1
2
3var arr=['aaa','bbb','ccc','aaa','eee']
var res=arr.lastIndexOf('aaa')
console.log(res) // 3
不影响原数组的数组方法之迭代方法forEach()
- forEach() 方法对数组的每个元素执行一次给定的函数。 没有返回值
1
2
3
4
5
6
7var arr=['aaa','bbb','ccc','ddd']
// arr.forEach((当前元素,当前元素索引,数组本身)=>{})
arr.forEach((item,index,arr)=>{
console.log(item) // aaa bbb ccc ddd
console.log(index) // 0 1 2 3
console.log(arr) // ['aaa', 'bbb', 'ccc', 'ddd']
})
数组去重小案例
1 | // 第一种方法 -利用for循环和indexOf |
12、字符串
12-1、创建字符串
- 我们创建字符串也分为两种方法 字面量和构造函数
- 字面量
1
var str = 'hello world'
- 构造函数创建
1
var str = new String('hello')
- 字符串长度
1
2
3var str = 'hello world'
// length 只读
console.log(str.length) // 11
12-2、字符集
ASCII 字符集
- 我们都知道 计算机只能存储0101010 这样的二进制数字
- 那么我们a-z / A-Z / $ / @ /…之类的内容也由二进制数字组成的
- 我们可以简单的理解为 a-z / A-Z / $ / @ /…之类的内容都有一个自己的编号 然后在计算机存储的时候,是存储的这些编号,我们看的时候,也是通过这些编号在解析成我们要看到的内容给我们看的
unicode 编码
- 我们看到了,ASCII只有这128个字符的编码结构
- 但是因为ASCII出现的比较早,而且是美国发明的,早先时候这些内容就够用了
- 因为存储一些英文的内容,传递一些英文的文章什么的都够用了
- 那么对于这个世界来说肯定不够用的
- 因为我们的汉字没有办法存储,包括一些其他国家的语言也没有办法存储
- 所以就出现了unicode编码,也叫(万国码,统一码)
- unicode对照表就是一个和ASCII一样的对照表 只不过变得很大很大,因为存储的内容特别的多
- 而且包含了世界上大部分国家的文字,所以我们的文字和字符现在在存储的时候,都是按照unicode编码转换成数字进行存储
- 我们的UTF-8就是一种8位的unicode字符集
12-3、小案例
统计字符串出现的次数
1 | var str="abcabcac" |
12-4、字符串常用方法
字符串常用方法之charAt()
- charAt(索引) 返回索引对应的字符
1
2
3var str = 'lmh'
var str1 = str.charAt(0)
console.log(str1) // l
字符串常用方法之charCodeAt()
- charCodeAt(索引) 返回索引对应的字符编码
1
2
3var str = 'lmh'
var str1 = str.charCodeAt(0)
console.log(str1) // 108
字符串常用方法之fromCharCode()
- fromCharCode(编码) 返回编码对应的字符
1
2
3
4
5var arr = []
for(var i=65;i<91;i++){
arr.push(String.fromCharCode(i))
}
console.log(arr) // ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
字符串常用方法之toUpperCase()
- toUpperCase(字符) 将字符串转为大写
1
2var str = "Lmh"
console.log(str.toUpperCase()) // LMH
字符串常用方法之toLowerCase()
- toLowerCase(字符) 将字符串转为小写
1
2var str = "Lmh"
console.log(str.toLowerCase()) // lmh
字符串常用方法之substr()
- substr(开始索引,长度) 截取
1
2
3var str = "abcdefg"
var str1 = str.substr(1,2)
console.log(str1) // bc
字符串常用方法之substring()
- substring(开始索引,结束索引) 截取
1
2
3var str = "abcdefg"
var str1 = str.substring(1,2)
console.log(str1) // b
字符串常用方法之slice()
- slice(开始索引,结束索引) 截取
1
2
3
4
5var str = "abcdefg"
var str1 = str.slice(1,2)
var str2 = str.slice(2)
console.log(str1) // b
console.log(str2) // cdefg
字符串常用方法之replace()
- replace(要替换的字符,替换成什么) 替换
1
2
3var str = "lmh"
var str1 = str.replace("mh","**")
console.log(str1) // l**
字符串常用方法之split()
- split(以什么字符分割) 分割 返回由分割后的字符组成的数组
1
2
3var str = "a|b|c|d|e|f"
var arr = str.split("|")
console.log(arr) // ['a', 'b', 'c', 'd', 'e', 'f']
字符串常用方法之concat()
- concat(需要拼接的字符) 字符串拼接
1
2
3var str = "lmh"
var str1 = str.concat("nb")
console.log(str1) //lmhnb
字符串常用方法之trim()
- trim() 去除首尾空格
- trimStart() 去除首空格
- trimLeft() 去除首空格
- trimEnd() 去除尾空格
- trimRight() 去除尾空格
1
2
3var str = " lmh "
console.log("|"+str+"|") // | lmh |
console.log("|"+str.trim()+"|") // |lmh|
12-5、json格式字符串
1 | // 字符串转对象 |
12-6、模板字符串
1 | var myHtml = ` |
13、数字
13-1、数字常用方法
数字常用方法之toFixed()
- toFixed(数字) 保留几位小数 返回的值是字符串
1
2var price = 123.147258369
console.log(price.toFixed(2)-0) // 123.14
数字常用方法之Math
- Math是一个对象 它身上有很多数字方法
Math.round() 四舍五入
1 | var num = 4.56 |
Math.ceil() 向上取整
1 | var num = 4.11 |
Math.floor() 向下取整
1 | var num = 4.99 |
Math.abs() 绝对值
1 | var num = -10 |
Math.sqrt() 平方根
1 | var num = 10 |
Math.pow(底数,指数) 次方
1 | console.log(Math.pow(3,3)) // 27 |
Math.max() 判断最大的数字 并返回
1 | console.log(Math.max(10,50,90,80)) // 90 |
Math.min() 判断最小的数字 并返回
1 | console.log(Math.min(10,50,90,80)) // 10 |
Math.PI 圆周率
1 | console.log(Math.PI) // 3.141592653589793 |
随机数小案例
1 | // 0~10 不包含10 |
14、Date
- js 提供的内置构造函数 专门用来获取时间
14-1、new Date()
new Date()在不传递参数的情况下是默认返回当前时间
1
2var time = new Date()
console.log(time) // Fri Jul 07 2023 16:26:26 GMT+0800 (中国标准时间)new Date() 在传入参数的时候,可以获取到一个你传递进去的时间
1
2var time = new Date('2018-09-09 16:27:26')
console.log(time) // Fri Jul 07 2023 16:27:26 GMT+0800 (中国标准时间)new Date() 传递的参数有多种情况
1、传递三个数字,第一个表示年,第二个表示月份 第三个表示日期 第四个表示小时 依次类推1
2var time = new Date(2023,10,3,10)
console.log(time) // Fri Nov 03 2023 10:00:00 GMT+0800 (中国标准时间)
14-2、时间对象常用方法
时间方法之getFullYear()
- getFullYear()方法用于获取年份
1
2var date = new Date()
console.log(date.getFullYear()) //2023
时间方法之getMonth()
- getMonth()方法用于获取月份 0-11====>1-12
1
2var date = new Date()
console.log(date.getMonth()+1)
时间方法之getDate()
- getDate()方法用于获取日期
1
2var date = new Date()
console.log(date.getDate())
时间方法之getDay()
- getDay()方法用于获取星期几 0=周日
1
2var date = new Date()
console.log(date.getDay())
时间方法之getHours()
- getHours()方法用于获取小时
1
2var date = new Date()
console.log(date.getHours())
时间方法之getMinutes()
- getMinutes()方法用于获取分钟
1
2var date = new Date()
console.log(date.getMinutes())
时间方法之getSeconds()
- getSeconds()方法用于获取秒数
1
2var date = new Date()
console.log(date.getSeconds())
时间方法之getMilliseconds()
- getMilliseconds()方法用于获取毫秒数
1
2var date = new Date()
console.log(date.getMilliseconds())
时间方法之getTime()
- getTime()方法用于获取时间戳
1
2var date = new Date()
console.log(date.getTime()) // 1688720266425
时间方法之toLocaleDateString()
- toLocaleDateString()方法用于获取年月日
1
2var date = new Date()
console.log(date..toLocaleDateString()) // 2023/7/7
时间方法之toLocaleTimeString()
- toLocaleTimeString()方法用于获取时分秒
1
2var date = new Date()
console.log(date..toLocaleTimeString()) // 17:19:02
时间方法之setFullYear()
- setFullYear()方法用于修改年份 返回时间戳
1
2var date = new Date()
console.log(date.setFullYear(2025))
时间方法之setMonth()
- setMonth()方法用于修改月份 0-11====>1-12 返回时间戳
1
2var date = new Date()
console.log(date.setMonth(5))
时间方法之setDate()
- setDate()方法用于修改日期 返回时间戳
1
2var date = new Date()
console.log(date.setDate())
时间方法之setHours()
- setHours()方法用于获取小时 返回时间戳
1
2var date = new Date()
console.log(date.setHours())
时间方法之setMinutes()
- setMinutes()方法用于获取分钟 返回时间戳
1
2var date = new Date()
console.log(date.setMinutes())
时间方法之setSeconds()
- setSeconds()方法用于获取秒数 返回时间戳
1
2var date = new Date()
console.log(date.setSeconds())
15、定时器
- 在js 里面 有两种定时器 倒计时定时器和间隔定时器
15-1、倒计时定时器
- 倒计时多少时间以后执行函数
- 语法:setTimeout(要执行的函数,多长时间以后执行)
- 会在你设定的时间以后 执行函数
1
2
3
4
5
6
7
8var time = setTimeout(()=>{
console.log("我执行了")
},1000)
console.log(time)
// 时间是按照毫秒进行计算的,1000毫秒就是1秒钟
// 所以会在页面打开一秒钟以后执行函数
// 只执行一次 就不在执行了
// 返回值是 当前这个定时器是页面中的第几个定时器
15-2、间隔定时器
- 倒计时多少时间以后执行函数
- 语法:setInterval(要执行的函数,多长时间以后执行)
- 会在你设定的时间以后 执行函数
1 | var time = setInterval(()=>{ |
15-3、清除定时器
clearTimeout(要清除的定时器) 用于清除setTimeout() 定时器
1
2
3
4var time=setTimeout(()=>{
console.log(222)
},5000)
clearTimeout(time)clearInterval(要清除的定时器) 用于清除setInterval() 定时器
1
2
3
4var time=setInterval(()=>{
console.log(222)
},5000)
clearInterval(time)
15-4、倒计时小案例
1 | var targetDate = new Date("2023/7/11") // 获取明天时间 |
16、初识BOM
- BOM(Browser Object Model) 浏览器对象模型
- 其实就是操作浏览器的一些能力
- 我们可以操作那些内容
- 获取一些浏览器的相关信息(窗口的大小)
- 操作浏览器进行页面的跳转
- 获取当前浏览器地址栏信息
- 操作浏览器的滚动条
- 浏览器的信息(浏览器的版本)
- 让浏览器出现一个弹出框(alert / confirm / prompt)
- BOM的核心就是window对象
- window是浏览器内置的一个对象,里面包含着操作浏览器的方法
16-1、获取浏览器窗口的尺寸
- innerHeight和innerWidth
- 这两个方法分别是用来获取浏览器创建的宽度和高度的(包含滚动条)
1
2
3
4
5
6// 获取高度
var windowHeight = window.innerHeight
console.log(windowHeight)
// 获取宽度
var windowWidth = window.innerWidth
console.log(windowWidth)
16-2、浏览器的弹窗框
alert(内容) 普通弹窗
1
window.alert("1111")
confirm(内容) 询问弹窗 返回值是true或false
1
2
3// 弹出一个带有确定和取消按钮的弹出框 点击确定返回true 点击取消返回false
var res = window.confirm("1111")
console.log(res)prompt() 输入框弹窗 返回值是你输入的内容
1
2// 弹出一个带有input输入框的弹出框 点击确定后 返回值是你输入的内容
var res = window.prompt("请输入你的用户名")
16-3、浏览器的地址信息
- 在window中有一个对象叫做location
- 就是专门用来存储浏览器的地址栏内的信息的
location.href
- location.href这个属性存储的是浏览器地址栏内url地址的信息
1
2console.log(window.location.href)
// 会把中文变成url编码格式 - location.href这个属性也可以给他赋值
1
2window.location.href="http://www.baidu.com"
// 这个就会跳转页面到后面你给的那个地址
location.reload
- location.reload() 这个方法会重新加载一遍页面 就相当于刷新是一个道理
- 注意:不要写在全局 否则会一直刷新
1
window.location.reload()
16-4、浏览器的onload事件
- 这个不再是对象了 而是一个事件
- 是在页面所以资源加载完毕后执行的
1
2
3
4window.onload = function(){
// 页面所有资源加载完毕执行(图片 视频 dom)
console.log('页面加载完毕时触发')
}
16-5、浏览器的onresize事件
- onresize 事件会在窗口大小发生变化时触发
1
2
3window.onresize = function(){
console.log('窗口大小发生改变时触发')
}
16-6、浏览器的onscroll事件
- onscroll 事件会在滚动条变化时触发
- 可以监听滚动条是否达到指定距离
1
2
3window.onscroll = function(){
console.log('滚动条滚动时触发')
}
16-7、浏览器的scrollTo事件
- scrollTo() 滚动到指定位置
1
2
3
4
5
6
7
8
9
10
11
12// 第一种写法
// window.scrollTo(数字,数字)
window.scrollTo(0,0)
// 参数1是在x轴滚动到那个位置
// 参数2是在y轴滚动到那个位置
// 第二种写法
// window.scrollTo(对象)
window.scrollTo({
left:0, // 表示y轴
top:0, // 表示x轴
})
16-8、小案例:回到顶部
1 | <style> |
16-9、浏览器打开和关闭标签页的方法
- open(地址) 用于打开一个指定地址的新标签页
1
window.open("http://www.baidu.com") // 打开百度
- close() 用于关闭当前标签页
1
window.close()
16-10、浏览器的历史记录
- window中有一个对象叫做history
- 是专门用来存储历史记录信息的
history.back
- history.back() 用来回退历史记录的 就是回到前一个页面 就相当于浏览器上的←按钮
1
2window.history.back()
// 前提是你要有上一条记录 不然就是一直在这个页面 也不会回退
history.forward
- history.forward() 是去到下一个历史记录里面 也即是去到下一个页面 就相当于浏览器上的→按钮
1
2window.history.forward()
// 前提是你要之前有过回退操作 不然你先就是最后一个页面 没有下一个
history.go
- history.go(正数或者负数) 正数的话是去到下一个页面 负数是回退到下一个页面
1
2window.history.go(-2) // 回退两个页面
// 前提是你要有两条条记录 不然就是一直在这个页面 也不会回退
17、本地存储
17-1、localStorage
- 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据
- 存储大小约为20MB
- 只支持字符串类型的存储
1
2
3
4
5
6
7
8
9
10// 增 localStorage.setItem(名称,内容)
localStorage.setItem("name","liuMingHao")
// 取 localStorage.getItem(名称)
localStorage.getItem("name")
// 删 localStorage.removeItem(名称)
localStorage.removeItem("name")
// 清空
localStorage.clear()
17-2、sessionStorage
- 数据在当前浏览器窗口关闭后自动删除
- 存储大小约为5MB
- 只支持字符串类型的存储
1
2
3
4
5
6
7
8
9
10// 增 2、sessionStorage.setItem(名称,内容)
2、sessionStorage.setItem("name","liuMingHao")
// 取 2、sessionStorage.getItem(名称)
2、sessionStorage.getItem("name")
// 删 2、sessionStorage.removeItem(名称)
2、sessionStorage.removeItem("name")
// 清空
2、sessionStorage.clear()
17-3、小案例:记住用户名
1 | <body> |
18、初识DOM
- DOM(Document Object Model):文档对象模型
- 其实就是操作html中的标签的一些能力
- 我们可以操作哪些内容
- 1、获取一个元素
- 2、移除一个元素
- 3、创建一个元素
- 4、向页面里面添加一个元素
- 5、给元素绑定一些事件
- 6、给元素添加一些css样式
- 7、…
- DOM的核心对象就是document对象
- document对象是浏览器内置的一个对象 里面存储着专门用来操作元素的各种方法
- DOM:页面中的标签 我们通过js获取到以后 就把这个对象叫做DOM对象
18-1、获取一个元素
- 通过js代码来获取页面中的标签
- 获取到以后我们就可以操作这些标签了
- 非常规获取元素 html body head
- 常规获取元素 id class tag…
非常规
1 | // 获取html标签 |
getElementById
- getElementById是通过标签的id名称来获取标签的
- 因为在一个页面中id是唯一的 所以获取到的就是元素
- 如果页面中有两个id名称相同的元素 getElementById只会匹配第一个
1
2
3
4
5
6
7
8<body>
<div id="box"></div>
<script>
var box = document.getElementById("box")
console.log(box) // <div id="box"></div>
</script>
</body>
<!-- 获取到的就是页面中的那个id为box的div标签 -->
getElementByClassName
- getElementByClassName是通过标签的class名称来获取标签的
- 可以获取多个class名称相同的元素
1
2
3
4
5
6
7
8
9
10
11
12
13<body>
<ul>
<li class="newClass"></li>
<li class="newClass"></li>
<li class="newClass"></li>
<li class="newClass"></li>
<li class="newClass"></li>
</ul>
<script>
var items = document.getElementByClassName("newClass")
console.log(items) // 打印的是一个伪数组
</script>
</body>
getElementsByTagName
- getElementsByTagName是通过标签名称来获取标签的
- 可以获取多个名称相同的元素
1
2
3
4
5
6
7
8
9
10
11
12
13<body>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<script>
var items = document.getElementsByTagName("li")
console.log(items) // 打印的是一个伪数组
</script>
</body>
getElementsByName
- getElementsByName是通过标签的name名称来获取标签的
- 可以获取多个name名称相同的元素
1
2
3
4
5
6
7
8<body>
<input type="text" name="username"/>
<input type="password" name="password"/>
<script>
var items = document.getElementsByName("username")
console.log(items) // 打印的是一个伪数组
</script>
</body>
querySelector
- 它可以在文档中查找第一个匹配指定选择器或者元素的元素。它返回找到的元素,如果没有找到则返回 null。
1
2
3
4
5
6
7<body>
<div id="box"></div>
<script>
var item = document.querySelect("#box")
console.log(item) // <div id="box"></div>
</script>
</body>
querySelectorAll
- 它可以在文档中查找所有符合条件的元素 它返回所有符合条件的元素,如果没有找到则返回 null。
1
2
3
4
5
6
7
8
9
10
11
12
13<body>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<script>
var items = document.querySelectorAll("ul li")
console.log(items) // 打印伪数组 NodeList(5) [li, li, li, li, li]
</script>
</body>
18-2、操作一个元素
自定义属性
1 | <div id="box" aaaa="2222222">hello</div> |
操作原生属性
1 | <body> |
小案例:密码可视
1 | <body> |
小案例:购物车全选
1 | <body> |
操作元素文本内容
innerHTML 可以获取或设置元素中所有的HTML代码,包括标签。
如果你需要操作元素中的HTML代码,那么使用 innerHTML。
也可以通过该方法改变元素内容 并且该方法解析html
1
2
3
4
5
6
7
8
9<body>
<div id="box">
<p>lmh</p>
</div>
<script>
box.innerHTML="<p>abcdefg</p>"
console.log(box.innerHTML) // 页面打印abcdefg
</script>
</body>innerText 仅能获取或设置元素中的纯文本内容,不包括标签。
如果你只需要操作元素中的纯文本内容,使用 innerText 就足够了
也可以通过该方法改变元素内容 但是该方法不解析html
1
2
3
4
5
6
7
8
9
10<body>
<div id="box">
<p>lmh</p>
</div>
<script>
console.log(box.innerText) // lmh
box.innerText="<p>abcdefg</p>"
console.log(box.innerText) // 页面 <p>abcdefg</p>
</script>
</body>value 能获取表单标签中的文本内容 不包括标签
也可以都过该方法改变元素内容
1
2
3
4
5
6
7
8<body>
<input type="text" id="username" value="hello">
<script>
console.log(username.value) // hello
username.value="lmh"
console.log(username.value) // lmh
</script>
</body>
小案例:渲染页面
1 | <body> |
操作元素样式
1 | <body> |
操作元素类名
1 | <body> |
小案例:选项卡
1 | <style> |
18-3、DOM节点
- DOM节点我们一般分为常用的三大类 元素节点 / 文本节点 / 属性节点
- 什么是分类 比如我们在获取元素的时候 通过各种方法获取到的我们叫做元素节点(标签节点)
- 比如我们标签里面写的文字 那就是文本节点
- 写在每一个标签上的属性 就是属性节点
元素节点
- 我们通过getElementBy…获取到的都是元素节点
属性节点
- 我们通过getAttribute获取到的都是属性节点
文本节点
- 我们通过innerHTML获取到的都是元素的文本节点
Document
- 我们一个页面最大的节点
- 但是我们不是一个元素节点
- 我只是一个作为承载使用的节点
- 我叫做根节点
html
- 我是一个页面最大的元素节点
- 我包含着页面所有的元素节点
- 我是最大的元素节点
- 我叫根元素节点
head/body/div/ul
- 我们都是普通的元素节点
- 只是所处的位置不一样
- 个自的功能不一样
- 我们都可能是父元素
- 也有可能是子元素
- 我们都叫做元素节点
文本内容
- 我是一段文本包含换行和空格
- 我也是一个节点
- 很多标签里面都包含我
- 我叫做文本节点
元素属性
- 我是一个元素身上的属性
- 我也是一个节点
- 很多元素上都有我
- 我叫做属性节点
注释内容
- 我是一段注释
- 我也是一个节点
- 我是单独书写的
- 不会显示在页面上
- 我叫做注释节点
18-4、获取DOM节点
1 | <body> |
18-5、操作DOM节点
- 我们所说的操作无法就是增删改查(CRUD)
- 创建一个节点(因为向页面中增加之前 我们需要先创建一个节点出来)
- 向页面增加一个节点
- 删除页面中的某一个节点
- 修改页面中的某一个节点
- 获取页面中的某一个节点
创建一个节点并插入到页面中
- createElement(标签名):用于创建一个元素节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14<body>
<div id="box">
<div id="child">11111</div>
</div>
<script>
var odiv = document.createElement("div")
odiv.innerHTML = "我是新建的节点"
odiv.id = "aaa"
odiv.style.backgroundColor="red"
console.log(odiv) // <div id="aaa" style="background-color: red;">我是新建的节点</div>
// appendChild(节点) 插入节点
box.appendChild(odiv) // 将创建的div插入到id为box的标签内
</script>
</body>
插入一个节点到页面中
- appendChild(节点):用于插入一个元素节点
1
2
3
4
5
6
7
8
9<body>
<div id="box">
<div id="child">11111</div>
</div>
<script>
var odiv = document.createElement("div")
box.appendChild(odiv) // 将创建的div插入到id为box的标签内
</script>
</body>
插入一个节点到标签内最前方
- insertBefore(要插入的节点,谁的前面) 用于插入一个元素节点 在标签内最前方
1
2
3
4
5
6
7
8
9<body>
<div id="box">
<div id="child">11111</div>
</div>
<script>
var odiv = document.createElement("div")
box.insertBefore(odiv,child) // 将创建的div插入到id为box的标签内 并且在id为child的标签前方
</script>
</body>
删除节点
- removeChild(节点对象)
1
2
3
4
5
6
7
8<body>
<div id="box">
<div id="child">11111</div>
</div>
<script>
box.removeChild(child) // id为child的标签会被删除
</script>
</body>
删除自己以及后代
- remove()标签以及他的后代都会被删除
1
2
3
4
5
6
7
8<body>
<div id="box">
<div id="child">11111</div>
</div>
<script>
box.remove() // id为box的标签以及他的后代会被删除
</script>
</body>
替换节点
- replaceChild(新的节点,老的节点)
1
2
3
4
5
6
7
8
9
10<body>
<div id="box">
<div id="child">11111</div>
</div>
<script>
var odiv2= document.createElement("div")
odiv2.innerHTML = "2222222"
box.replaceChild(odiv2,child)
</script>
</body>
克隆节点
- cloneNode() false 不克隆后代 true克隆后代
1
2
3
4
5
6
7
8
9
10<body>
<div id="box">
<div id="child">11111</div>
</div>
<script>
var oCloneBox = box.cloneNode(true)
oCloneBox.id="box2"
document.body.appendChild(oCloneBox)
</script>
</body>
动态删除
1 | <body> |
18-6、获取元素尺寸
- 就是获取元素的’占地面积’
offsetWidth和offsetHeight
- offsetWidth:获取的是元素 内容 + padding + border的宽度
- offsetHeight:获取的是元素 内容 + padding + border的高度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<style>
div{
width: 100px;
height: 100px;
padding: 10px;
border: 5px solid red;
background-color: yellow;
}
</style>
<body>
<div id="box"></div>
<script>
console.log(box.offsetHeight,box.offsetWidth) // 130 130
</script>
</body>
clientWidth和clientHeight
- clientWidth:获取的是元素 内容 + padding的宽度
- clientHeight:获取的是元素 内容 + padding的高度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<style>
div{
width: 100px;
height: 100px;
padding: 10px;
border: 5px solid red;
background-color: yellow;
}
</style>
<body>
<div id="box"></div>
<script>
console.log(box.clientWidth,box.clientHeight) // 120 120
</script>
</body>
注意
- 获取到的尺寸是没有单位的数字
- 当元素在页面中不占位置的时候 获取到的是0
18-7、获取元素偏移量
- offsetLeft和offsetTop参考有定位的父级
- 如果父级元素都没有定位 偏移量相对于body
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41<style>
*{
margin: 0;
padding: 0;
}
#box{
width: 500px;
height: 500px;
background-color: yellow;
overflow: hidden;
}
#myParent{
width: 300px;
height: 300px;
background-color: blue;
overflow: hidden;
}
#child{
width: 100px;
height: 100px;
background-color: red;
overflow: hidden;
}
div{
margin: 50px;
}
</style>
<body>
<div id="box">
<div id="myParent">
<div id="child"></div>
</div>
</div>
<script>
console.log(child.offsetLeft,child.offsetTop) // 150 150
console.log(myParent.offsetLeft,myParent.offsetTop) // 100 100
console.log(box.offsetLeft,box.offsetTop) // 50 50
// offsetLeft和offsetTop参考有定位的父级
// 如果父级元素都没有定位 偏移量相对于body
</script>
</body>
18-8、获取可视窗口的尺寸
- 不包括滚动条
1
2console.log("宽度",document.documentElement.clientWidth);
console.log("高度",document.documentElement.clientHeight);
18-9、获取页面滚动条距离顶部的距离
- document.documentElement.scrollTop 是 JavaScript 中用来获取页面滚动条距离顶部的距离。它通过访问 HTML 文档的 documentElement 对象的 scrollTop 属性来获取。
19、初识事件
- 一个事件有什么东西组成
1、触发谁的事件:事件源
2、触发什么事件:事件类型
3、触发以后做什么:事件处理函数4、我们想要在点击div以后做什么事情 就把我们要做的事情写在事件处理函数里面1
2
3
4
5var oDiv = document.querySelector('div')
oDiv.onclick = function() {}
// 谁来触发事件 => oDiv => 这个事件的事件源就是oDiv
// 触发什么事件 => onclick => 这个事件类型就是click
// 触发之后做什么 => function() {} => 这个事件的处理函数1
2
3
4
5
6var oDiv = document.querySelector('div')
oDiv.onclick = function() {
console.log('你点击了div')
}
// 当我点击div的时候 就会执行事件处理函数内部的代码
// 每点击一次 就会执行一次事件处理函数
19-1、addEventListener()
- addEventListener属于dom2的事件 onclick属于dom0的事件
- 和普通点击事件onclick不同的是 他可以同时绑定多个事件 并且依次执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<body>
<div id="box">aaa</div>
<div id="box2">bbb</div>
<script>
// onclick 绑定多个事件 只会执行最后一个
box.onclick=function(){
console.log("11111")
}
box.onclick=function(){
console.log("22222")
}
// 1、addEventListener 会依次执行事件
box2.addEventListener("click", function(){
console.log("11111")
})
box2.addEventListener("click", function(){
console.log("22222")
})
box2.addEventListener("click", function(){
console.log("3333")
})
</script>
</body>
19-2、事件解绑
1 | <body> |
19-3、事件类型
鼠标事件
onclick 鼠标左键点击事件
1
元素.onclick = function(){事件处理函数}
ondblclick 鼠标左键双击事件
1
元素.ondblclick = function(){事件处理函数}
oncontextmenu 鼠标右键单击事件
1
元素.oncontextmenu = function(){事件处理函数}
onmousedown 鼠标左键按下事件
1
元素.onmousedown = function(){事件处理函数}
onmousemove 鼠标在元素内移动触发事件
1
元素.onmousemove = function(){事件处理函数}
onmouseup 鼠标左键抬起事件
1
元素.onmouseup = function(){事件处理函数}
onmouseover 鼠标移入事件 在后代元素上会触发
1
元素.onmouseover = function(){事件处理函数}
onmouseout 鼠标移出事件 在后代元素上会触发
1
元素.onmouseout = function(){事件处理函数}
onmouseenter 鼠标移入事件 在后代元素上不会触发
1
元素.onmouseenter = function(){事件处理函数}
onmouseleave 鼠标移出事件 在后代元素上不会触发
1
元素.onmouseleave = function(){事件处理函数}
键盘事件
onkeydown 按下键盘事件
1
元素.onkeydown = function(){事件处理函数}
onkeyup 抬起键盘事件
1
元素.onkeyup = function(){事件处理函数}
表单事件
onfocus 获取焦点事件
1
元素.onfocus = function(){事件处理函数}
onblur 失去焦点事件
1
元素.onfocus = function(){事件处理函数}
onchange 获取焦点 失去焦点的对比里面的内容不一样才会触发事件
1
元素.onchange = function(){事件处理函数}
oninput 事件在用户输入时触发。 该事件元素的值发生改变时触发。
1
元素.onchange = function(){事件处理函数}
onsubmit 只能表单上使用,提交表单前会触发 必须有form标签 input的类型必须是submit
1
元素.onchange = function(){事件处理函数}
onreset 用来重置表单
1
元素.onchange = function(){事件处理函数}
触摸事件
ontouchstart 触摸事件
1
元素.ontouchstart = function(){事件处理函数}
ontouchmove 触摸后滑动触发事件
1
元素.ontouchmove = function(){事件处理函数}
ontouchend 离开触发事件
1
元素.ontouchend = function(){事件处理函数}
ontouchcancel 强制离开触摸事件
1
元素.ontouchcancel = function(){事件处理函数}
19-4、事件对象
- 什么是事件对象?
- 就是当你触发了一个事件以后 对该事件的一些描述信息
- 例如
1、你触发一个点击事件以后 你点在哪个位置了 坐标是多少
2、你触发一个键盘事件的时候 你按的是哪个按钮 - 每一个事件都会有一个对应的对象来描述这些信息 我们就把这个对象叫做事件对象
- 浏览器给了我们一个黑盒子 叫做window.event 就是对事件信息的所有描述
1、你点在了0,0位置 那么就得到这个事件对象里面对应的就会有这个点位的属性
2、你点在10,10位置 那么你就会得到这个事件对象里面对应的就会有这个点位的属性1
2
3
4
5
6
7
8
9<body>
<input type="text" id="user">
<script>
// 事件对象就是回调函数中的实参传过来的
user.onkeyup = function(event){
console.log(event) // 打印一个对象
}
</script>
</body>
鼠标事件
1 | <body> |
小案例:鼠标跟随
1 | <style> |
小案例:鼠标拖拽
1 | <style> |
19-5、事件的传播
- 当元素触发一个事件的时候 其父元素也会触发相同的事件 父元素的父元素也会触发相同的事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47<body>
<div id="outer">
<div id="center">
<div id="inner"></div>
</div>
</div>
<script>
// 当我们点击inner的点击事件时 他父级center点击事件也会被触发 它父级父级outer的点击事件也会被触发
inner.onclick = function(){
console.log('inner')
}
center.onclick = function(){
console.log('center')
}
outer.onclick = function(){
console.log('outer')
}
// 标准的dom事件
// 捕获:window => document => body =>.....
// 目标:inner
// 冒泡:center => outer => body => document => window
// 默认情况 只在冒泡触发
// 冒泡阶段触发
inner.addEventListener("click",function(){
console.log("inner")
})
center.addEventListener("click",function(){
console.log("center")
})
outer.addEventListener("click",function(){
console.log("outer")
})
// 捕获阶段触发
// 按照dom2事件绑定 并进行配置 才能看到捕获的回调函数被触发
inner.addEventListener("click",function(){
console.log("inner")
},true)
center.addEventListener("click",function(){
console.log("center")
},true)
outer.addEventListener("click",function(){
console.log("outer")
},true)
</script>
</body>
阻止事件冒泡
1 | 元素.onclick=function(event){ |
阻止默认行为
1 | // dom0 的方法 我们可以使用return的方式来阻止默认行为 |
19-6、小案例:自定义右键菜单
1 | <style> |
19-7、事件委托
- 就是把我要做的事情委托给别人来做
- 因为我们的冒泡机制 点击子元素的时候 也会同步触发元素的相同事件
- 所以我们就可以把子元素的事件委托给父元素来做
事件触发
- 点击子元素的时候 不管子元素有没有点击事件 只要父元素有点击事件 那么就可以触发父元素的点击事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var oul = document.querySelector("ul")
oul.addEventListener("click",function(e){
console.log('我是ul的点击事件,我触发了')
})
// 这段代码 当你点击ul的时候肯定会触发
// 但是当你点击li的时候 其实也会触发
</script>
</body>
target
- target这个属性是对事件对象里面的属性 表示你点击的目标
- 当你触发点击事件的时候 你点击在哪个元素上 target就是哪个元素
- 这个target也不兼容 在IE下要使用srcElement
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<body>
<ul id="list">
<li id="a" flagIndex="0">11111</li>
<li id="a" flagIndex="1">11111</li>
<li id="a" flagIndex="2">11111</li>
<li id="a" flagIndex="3">11111</li>
<li id="a" flagIndex="4">11111</li>
</ul>
<script>
list.onclick = function(evt){
var index= evt.target.getAttribute('flagIndex') // 获取自定义属性
a[index].style.color = 'red'//根据下标设置样式。
}
/*
优点:
减少多个函数的绑定的性能损耗
动态添加li 也会有事件
*/
</script>
</body>
20、初始正则表达式
- 创建一个正则表达式
1
2
3
4// 1、字面量
var reg = /abc/ // 判断字符串是否包含abc
// 2、内置构造函数
var reg2 = new RegExp("abc") - 判断一个字符串中是否包含abc
1
2
3var reg = /abc/
var str = 'abcde'
console.log(reg.test(str)); // true
20-1、元字符
基本元字符
1 | // \d 一位数字(0-9) |
边界符
1 | // ^ 开头 |
限定符
1 | // * 0~多次 |
特殊字符
1 | // () 整体 |
捕获
1 | // test() 返回布尔值 |
标识符
1 | var dateStr = "time is 2029-01-01 12:20:20 to 2029-11-11 12:20:20" |
正则表达式的两大特性
- 1、懒惰 捕获的时候,正则记不住上一次的位置 解决:使用g 全局
- 2、贪婪 解决:?将正则转为非贪婪模式
1
2var reg = /\d{1,4}/
console.log(reg.exec("a1234567aa")); // 其实捕获一个数字就可以 但是因为正则的贪婪 所以它捕获了4个数字
20-2、正则与字符串方法
1 | // replace不搭配正则 |
20-3、小案例:密码强度验证
1 | <style> |
21、this
this是什么?
21-1、this指向
1 | <body> |
21-2、改变this指向
- call,apply,bind的区别:
call和bind的参数是依次传参,一一对应的;
但apply只有两个参数,第二个参数为数组;
bind方法不会主动执行函数 而是返回一个函数
1 | var obj1 = { |
22、ES6
- 我们所说的ES5和ES6其实就是在js语法的发展过程中的一个版本而已
- ECMAScript就是js的语法
- 1、以前的版本没有某些功能
- 2、 在ES5这个版本的时候增加一些功能
- 3、在ES6这个版本的时候增加一些功能
- 因为浏览器是浏览器厂商生产的
- 1、ECMAScript发布了新的功能都可以比较完善的支持了
- 2、这个过程是需要时间的
- 3、所以到现在 基本大部分浏览器都可以比较完善的支持了
- 4、只不过有些浏览器还是不能全部支持
- 5、这就出现了兼容性问题
- 6、所以我们写代码的时候就要考虑哪些方法是ES5或者ES6的 看看是不是浏览器都支持
22-1、let和const关键字
- 我们以前都是使用var关键字来声明变量
- 在ES6的时候 多了两个关键字let和const 也是用来声明变量的
- 只不过和var有些区别
声明方式 变量提升 暂时性死区 重复声明 初始值 作用域
var 允许 不存在 允许 不需要 非块级
let 不允许 存在 不允许 不需要 块级
const 不允许 存在 不允许 需要 块级
1 | // 1、不允许变量提升 |
22-2、箭头函数
- 1、()可以省略 只有在一个参数的时候才可以省略()
- 2、{}可以省略 只有一句代码或者只有返回值的时候 可以省略return和{}
- 3、没有arguments
- 4、箭头函数没有this 箭头函数this是父级作用域的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72<body>
<input type="text" id="input">
<script>
// 普通函数声明
var test1 = function(){
console.log("111111")
}
test1()
// 箭头函数声明
var test2 = () =>{
console.log("2222")
}
test2()
// 1、()可以省略 只有在一个参数的时候才可以省略()
var test = a =>{
console.log(a) // 1111
}
test(1111)
// 2、{}可以省略 只有一句代码或者只有返回值的时候 可以省略return和{}
var test = a => a*100
console.log(test(10)) // 1000
// 3、没有arguments
// 普通函数
var test = function(a,b,c){
console.log(arguments[0]) // aaa
}
test("aaa","bbb","ccc")
// 箭头函数
var test = (a,b,c) =>{
console.log(arguments[0]) // 报错 guments is not defined
}
test("aaa","bbb","ccc")
// 4、箭头函数没有this 箭头函数this是父级作用域的
let ipt=document.querySelector("input")
// 普通函数
ipt.oninput = function(){
setTimeout(function(){
console.log(this) // 打印window对象
// 为什么打印window而不是input标签呢
// 因为在定时器中 this是指向window
},1000)
}
// 解决方法
ipt.oninput = function(){
var that = this // 我们可以先把this存起来
setTimeout(function(){
console.log(that) // <input type="text" id="input">
},1000)
}
// 箭头函数
ipt.oninput = function(){
setTimeout(()=>{
console.log(this) // <input type="text" id="input">
// 为什么打印input标签而不是window呢
// 因为箭头函数执行外层上下文中的函数 所以指向input标签
},1000)
}
// 函数的默认参数
var test=(a=1,b=2)=>{
return a+b
}
console.log(test()) // 3
</script>
</body>
22-3、解构赋值
- 快速的从对象和数组中获取里面的成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41// 数组的解构
var arr = ['xiaoming','teichui','shanzhen']
let [x,y,z]=arr
console.log(x,y,z) // xiaoming teichui shanzhen
// 两个数字交换
var a = 10
var b = 20
var [b,a] = [a,b]
console.log(a,b) // 20 10
// 对象的解构
var obj = {
name:"lmh",
age:100,
location:"hebei"
}
// 解构赋值并重命名
let {name,age,location:site} = obj
console.log(name,age,site) // lmh 100 hebei
// 复杂结构
var obj = {
name:"lmh",
age:100,
location:{
province:"hebei",
city:"cangzhou"
},
hobby:[111,222,333]
}
var {
name,
age,
location:{
province,
city
},
hobby:[a,b,c]
} = obj
console.log(name,age,province,city,a,b,c) // lmh 100 hebei cangzhou 111 222 333
22-4、展开运算符
- 数组用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29var a = [1,2,3]
var b = [4,5,6]
console.log(a.concat(b)) // 不使用展示运算符 [1, 2, 3, 4, 5, 6]
var c = [...a,...b]
console.log(c) // 使用展开运算符 [1, 2, 3, 4, 5, 6]
// 复制
var a = [1,2,3]
var b = [...a]
console.log(b) // [1,2,3]
// 展开 实参-形参
const test = (...arr) => {
console.log(arr) // [1, 2, 3, 4, 5]
}
test(1,2,3,4,5)
const arr = [1,2,3,4,5]
const test1 = (...arr) => {
console.log(arr) // 1,2,3,4,5
}
test1(...arr)
// 伪数组转换
function test(){
console.log([...arguments]) // [1, 2, 3, 4, 5]
}
test(1,2,3,4,5) - 对象用法
1
2
3
4
5
6
7
8
9
10
11
12var obj1 = {
name:"lmh",
age:100
}
var obj2 = {
location:"hebei"
}
var obj = { // 如果两个对象中有同名的属性 则会被覆盖 假如 obj1中有name属性 obj2中同样也有 那么obj2中的name属性值就会覆盖obj1里面的name属性值
...obj1,
...obj2
}
console.log(obj) // {name: 'lmh', age: 100, location: 'hebei'}
22-5、面向对象
- 首先 我们要明确 面向对象不是语法 是一个思想 是一种编程模式
- 面向(脸) 向(朝着)
- 面向过程:脸朝着过程 => 关注着过程的编程模式
- 面向对象:脸朝着对象 => 关注着对象的编程模式
- 实现一个效果
1、在面向过程的时候 我们要关注每一个元素 每一个元素之间的关系 顺序
2、在面向过程的时候 我们要关注的就是找到一个对象来帮我做这个事情 我等待结果 - 我们以前的编程思想是 每一个功能 都按照需求一步一步的逐步完成
创建对象的方式
1 | // 工厂函数 |
构造函数和普通函数的区别
- 构造函数:
- new Fn()
- 构造函数内部会创建一个新的对象,即f的实例
- 函数内部的this指向 新创建的f的实例
- 默认的返回值是f的实例
- 普通函数:
- fn()
- 在调用函数的内部不会创建新的对象
- 函数内部的this指向调用函数的对象(如果没有对象调用,默认是window)
- 返回值由return语句决定
面向对象的原型
1 | <body> |
22-6、class
- 在js的类中,可分为三种方法,constructor构造方法、静态方法与普通方法。
一、constructor构造方法
1.概念
- 类的作用在于构建对象,而constructor构造方法就是用于构建对象实例。
2.使用方法
- 在使用new关键字生成对象时,constructor方法会被执行,最终return的结果就是生成的对象实例。
- 当一个类没有constructor方法时会自动生成一个空的constructor方法,返回结果为空。
- 用new关键字实例化对象时传入的参数会做为constructor构造函数的参数传入。
1
2
3
4
5
6class Point {
constructor(name) {
console.log(name);
}
}
new Point('testObj'); // testObj
二、普通方法
- 1.概念
class类的普通方法可以看作是构造函数的另一种写法,相当于在类的prototype属性上边定义方法。
1 | class Point { |
- 2.使用方法
(1).该类实例化的对象上使用此方法
1
2
3
4
5
6
7class Point {
toString() {
// ...
}
}
let obj = new Point();
obj.toString();(2).直接通过该类的prototype调用此方法
1
2
3
4
5
6class Point {
toString() {
// ...
}
}
Point.prototype.toString();(3).通过子类的__proto__调用
1
2
3
4
5
6
7
8class Foo {
commonMethod() {
return 'hello';
}
}
class Bar extends Foo {
}
Bar.__proto__.prototype.commonMethod();
三、静态方法
1.概念
- 类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用(通过类调用指在该类之外调用),这就称为“静态方法”。
2.使用方法
静态方法只能在当前类上调用,不能被该类的实例对象调用。父类的静态方法可以被子类继承。
因此静态方法被调用的方式一共有三种(三种调用方式都在下面一段代码中使用到了,请耐心阅读):
父类直接调用
子类继承父类后调用
子类通过super对象调用
1 | class Foo { |
注:在class类中,它的静态属性与普通属性允许重名。但是不建议重名,这里就不给出demo了。
22-7、继承
原型链继承(通过改变原型的指向实现继承)
- 原型继承===只能继承原型上的方法
- 缺点:
- 1、不能传递参数,
- 2、如果父类的属性是引用类型,子类实列修改了该属性,其他的子类实列会共享该属性。
1
2
3function Person(){}
function Student(){}
Student.prototype = new Person();
借用父级构造函数实现继承(通过call修改this指向)(不会继承prototype)
- 构造函数继承===只能继承属性
- 缺点:
- 1、子类无法继承父类在原型链上的属性和方法。
- 2,每个实例都拷贝一份,占用内存大,尤其是方法过多的时候 (函数复用又无从谈起了,本来我们用prototype就是解决复用问题的)
- 优点:
- 1、解决了子类实列修改了父类属性,其他的子类实列会共享该属性的问题
1
2
3
4
5
6
7
8
9
10function Person(name,age){
this.name = name;
this.age = age
}
function Student(name,age,grade){
Person.call(this,name,age); // 通过call继承
this.grade = grade
}
var obj = new Student("lmh",100,100)
console.log(obj) // Student {name: 'lmh', age: 100, grade: 100}
- 1、解决了子类实列修改了父类属性,其他的子类实列会共享该属性的问题
组合继承(原型链继承+借用构造函数继承)
- 组合继承是js最常用的继承模式,
- 组合继承===构造函数继承 + 原型继承
- 缺点:
- 1、组合继承最大的问题就是无论在什么情况下,都会调用两次构造函数:一次是在创建子类型原型时,另一次是在子类构造函数内部。
寄生组合继承(常用)
- 寄生组合继承就是避免两次调用父类构造函数,通过赋值直接继承父类的原型
- 寄生组合继承 就是 组合继承 + 原型式继承 的结合体 js内部会把这个寄生组成封装成 extend
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18function Father(name) {
this.name = name
this.hbody = ['吃饭', '睡觉', '打豆豆']
}
Father.prototype.getName = function() {
console.log(this.name)
}
function Son(name, age) {
Father.call(this, name)
this.age = age
}
Son.prototype = Object.create(Father.prototype)
// 手动挂上构造器,指向自己的构造函数
Son.prototype.construtor = Son
let S = new Son('亚瑟', '22')
let F = new Father('李白')
console.log('寄生组合1', S)
console.log('寄生组合2', F)
22-8、ES6继承
- 通过extends继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class Person {
constructor() {
this.name = "aaaa"
this.age = 18
}
say() {
console.log(this.name, "hello")
}
}
// 子类
// extends 原型继承
class Student extends Person {
constructor(name, age) {
super(name, age) // 继承属性
}
say() { //继承方法 起相同的方法名就行 子级直接覆盖父级
super.say() // 调用父级的方法
console.log(this.name, "你好")
}
}
var obj = new Student()
console.log(obj) // Student {name: 'aaaa', age: 18}
obj.say()
23、初始前后端交互
23-1、ajax
- 是指一种创建交互式网页应用的网页开发技术,用于浏览器和服务器之间进行数据交互。
- AJAX在浏览器与Web服务器之间使用异步数据传输(HTTP请求),这样就可使网页从服务器请求少量的信息,而不是整个页面。
- AJAX描述了一种主要使用脚本操作HTTP的Web应用架构,AJAX应用的主要特点是使用脚本操纵HTTP和Web服务器进行数据交换,不会导致页面重载。
ajax的优势
1、不需要插件的支持 原生js就可以使用
2、用户体验好(不需要刷新页面就可以更新数据)
3、减轻服务端和宽带的负担
4、缺点:搜索引擎的支持度不够 因为数据都不在页面上 搜索引擎搜索不到
ajax的使用
- 在js中有内置的构造函数来创建ajax对象
- 创建ajax对象以后 我们就使用ajax对象的方法去发生请求和接收响应
1
2
3
4
5
6
7
8
9
10
11
12
13// ajax === async javascript and xml (闭合标签)
// 1、创建XHR new XMLHttpRequest()
var xhr = new XMLHttpRequest()
// 2、配置 open(请求方式 请求地址 是否异步(默认异步))
xhr.open('GET',"")
// 3、send 发生请求
xhr.send()
// 4、接受数据 注册一个事件
xhr.onreadystatechange = function(){
if(xhr.readyState===4){
console.log("数据解析完成")
}
}
ajax状态码
- ajax状态码 - xhr.readyState
- 是用来表示一个ajax请求的全部过程中的某一个状态
- readyState === 0 表示未初始化完成 也就是open方法还没执行
- readyState === 1 表示配置信息已经完成 也就是执行完open方法之后
- readyState === 2 表示send方法已经执行完成
- readyState === 3 表示正在解析响应内容
- readyState === 4 表示响应内容已经解析完毕 可以在客户端使用了
- 这个时候我们就会发现 当一个ajax请求的全部过程中 只有当readyState === 4的时候 我们才可以正常使用服务端给我们的数据
ajax中的readyStateChange
- 在ajax对象中有一个事件 叫做readyStateChange事件
- 这个事件时专门用来监听ajax对象的readStateChange值改变的行为
- 也就是说只要readyStateChange的值发生变化了 那么就会触发该事件
- 所以我们就在这个事件中来监听ajax的readyState是不是到4了
1
2
3
4
5
6
7
8
9
10
11
12var xhr = new XMLHttpRequest()
xhr.open('GET',"")
xhr.send()
xhr.onreadystatechange = function(){
// 每次readyState改变的时候都会触发该事件
// 我们就在这里判断readyState的值是不是到4
// 并且http的状态码是不是200~299
if(xhr.readyState===4&&xhr.status===200){
// 这里表示验证通过
// 我们就可以获取服务端给我们响应的内容了
}
}
ajax同步异步
1 | // ajax 同步异步 |
请求方式
- get 偏向于获取数据
- post 偏向于提交数据
- put 偏向于更新数据(全部)
- delete 偏向于删除信息
- patch 偏向于部分修改
- header 偏向于获取服务器头信息
- options 偏向于获取服务器的设备信息
- connect 保留请求方式
1 | <body> |
ajax封装
- 封装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31function ajax(options) {
// 创建一个XMLHttpRequest对象
var xhr = new XMLHttpRequest();
// 设置请求方法和URL
xhr.open(options.method || 'GET', options.url);
// 设置请求头
if (options.headers) {
for (var key in options.headers) {
xhr.setRequestHeader(key, options.headers[key]);
}
}
// 设置请求完成的回调函数
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
options.success && options.success(xhr.responseText);
} else {
options.error && options.error(xhr.statusText);
}
};
// 设置请求错误的回调函数
xhr.onerror = function () {
options.error && options.error(xhr.statusText);
};
// 发送请求
xhr.send(options.data);
} - 使用
1
2
3
4
5
6
7
8
9
10
11
12ajax({
url:"http://localhost:3000/user",
method:"GET",
data:{},
headers:{},
success: function(res){
console.log(res)
},
error: function(err){
console.log(err)
}
})
回调地狱
- 当一个回调函数嵌套一个回调函数的时候
- 就会出现一个嵌套结构
- 当嵌套的多了就会出现回调地狱的情况
- 比如我们发送三个ajax请求
- 第一个正常发送
- 第二个请求需要第一个请求的结果中的某一个值作为参数
- 第三个请求需要第二个请求的结果中的某一个值作为参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17ajax({
url:"我是第一个请求",
success(res){
// 现在发送第二个请求
ajax({
url:"我的第二个请求",
data:{a:res.a,b:res.b}
success(res2){
// 进行第三个请求
ajax({
url:"我的第三个请求",
data:{a:res2.a,b:res2.b}
})
}
})
}
})
23-2、Promise
- promise是一个ES6语法
- promise是解决是一个专门用来解决异步回调地狱的问题的方法。
- Promise对象有两个特点:
- 1)对象的状态不受外界的影响。
- 2)状态一旦改变,便不会再次改变。而且它的状态改变只会由(pending->fulfilled、pending->rejected)并且这两种情况只要发生其中一个,状态便固定了。
- Promise的优点:
- 支持链式调用(可以将异步操作以同步的方式显示出来)避免回调地狱
promise基础语法
1 | // Promise 构造函数 |
Promise封装ajax 并解决回调地狱
- promise封装ajax
1
2
3
4
5
6
7
8
9
10
11
12
13
14function pAjax(options){
return new Promise((resolve,reject)=>{
// ajax方法来自于23-1、ajax中ajax的封装
ajax({
...options,
success:(res)=>{
resolve(res)
},
error:(err)=>{
reject(err)
}
})
})
} - Promise是一种用于处理异步操作的对象,它可以解决回调地狱问题。回调地狱指的是在异步操作中,多个回调函数嵌套调用,导致代码可读性差、难以维护的情况。
- Promise通过链式调用的方式,使得代码结构更加清晰、可读性更高,同时可以有效地解决回调地狱问题。下面是使用Promise解决回调地狱问题的示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29pAjax({
url:"路径1",
data:{
author:"值1"
}
}).then(result1 => {
// 处理result1
return pAjax({
url:"路径2",
data:{
author:"值2"
}
})
})
.then(result2 => {
// 处理result2
return pAjax({
url:"路径3",
data:{
author:"值3"
}
})
})
.then(result3 => {
// 处理result3
})
.catch(error => {
// 处理错误
});
async/await
- async/await 是一个es7的语法
- 这个语法是回调地狱的终极解决方案
- 语法
1
2
3async function fn(){
const res = await promise对象
} - 这是一个特殊的函数方式
- 可以await一个promise对象
- 可以把异步代码写的看起来想同步代码
- 只要一个promise对象 那么我们就可以使用async/await来书写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import {pAjax} from "./utilPromise.js"
async function test(){
// await 后面可以只能写同步代码和promise对象
var res = await pAjax({
url: "http://localhost:3000/news",
data: {
author: "lmh"
}
})
console.log(JSON.parse(res)) // 先执行
console.log(222) // 后执行
return JSON.parse(res)
}
test().then((res)=>{
console.log("返回结果",res)
}) - 解决回调地狱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import {pAjax} from "./utilPromise.js"
async function test(){
// await 后面可以只能写同步代码和promise对象
var res = await pAjax({
url: "http://localhost:3000/news",
data: { author: "lmh" }
})
var res1 = pAjax({
url:"http://localhost:3000/comments",
data:{ newsId:JSON.parse(res)[0].id }
})
return res1
}
test().then((res)=>{
console.log("返回结果",res)
})
23-3、Fetch请求
- XMLHttpRequest是一个粗糙的API 配置和调用方法非常混乱 而且基于事件的异步模型写起来不友好
- Fetch API 提供了一个获取资源的接口(包括跨域请求),用于取代传统的XMLHttpRequest的,在 JavaScript 脚本里面发出 HTTP 请求。
- 目前还没有被所有浏览器支持,如果考虑低版本浏览器的问题的话,引入https://github.com/github/fetch/blob/master/fetch.js即可兼容
- Fetch API是基于promise的设计,返回的是Promise对象,它是为了取代传统xhr的不合理的写法而生
- fetch()使用 Promise,不使用回调函数,因此大大简化了写法,写起来更简洁。
- fetch()采用模块化设计,API 分散在多个对象上(Response 对象、Request 对象、Headers 对象),更合理一些;相比之下,XMLHttpRequest 的 API 设计并不是很好,输入、输出、状态都在同一个接口管理,容易写出非常混乱的代码。
- fetch()通过数据流(Stream对象)处理数据,可以分块读取,有利于提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。XMLHttpRequest对象不支持数据流,所有的数据必须放在缓存里,不支持分块读取,必须等待全部拿到后,再一次性吐出来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99<body>
<button id="myget">get</button>
<button id="mypost">post</button>
<button id="myput">put</button>
<button id="mydelete">delete</button>
<button id="mypatch">patch</button>
<script>
// get请求---获取数据
myget.onclick = function () {
fetch("http://localhost:3000/user11")
.then((res) => {
if (res.ok) {
return res.json()
} else {
// 拒绝承诺
return Promise.reject({
status: res.status,
statusText: res.statusText
})
}
}).then((res) => {
console.log(res)
}).catch((err) => {
console.log(err)
})
}
// get请求传参---获取数据
// var user = "xiaoMing"
// myget.onclick = function () {
// fetch(`http://localhost:3000/user?username=${user}`).then((res)=>{
// return res.json()
// }).then((res)=>{
// console.log(res)
// })
// }
// post请求---提交数据
mypost.onclick = function () {
fetch("http://localhost:3000/user", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
username: "aaaa",
password: 1111
})
}).then((res) => {
return res.json()
}).then((res) => {
console.log(res)
})
}
// put请求---修改数据
myput.onclick = function () {
fetch("http://localhost:3000/user/4", {
method: "PUT",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
username: "bbbb",
})
}).then((res) => {
return res.json()
}).then((res) => {
console.log(res)
})
}
// patch请求---修改部分数据
mypatch.onclick = function () {
fetch("http://localhost:3000/user/5", {
method: "PATCH",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
username: "ccccc",
})
}).then((res) => {
return res.json()
}).then((res) => {
console.log(res)
})
}
// delete请求---删除数据
mydelete.onclick = function () {
fetch("http://localhost:3000/user/2", {
method: "DELETE",
}).then((res) => {
return res.json()
}).then((res) => {
console.log(res)
})
}
</script>
</body>
24、Cookie
- 只能存储文本
- 单条存储有大小限制4KB左右
- 数量限制(一般浏览器 限制大概在50条左右)
- 读取有域名限制 不可跨域读取 只能由来自写入cookie的同一域名的网页可进行读取 简单的讲就是 哪个服务器发给你的cookie 只有哪个服务器有权利读取
- 时效限制:每个cookie都有时效 默认的有效期是 会话级别 就是当前浏览器关闭 那么cookie立即销毁 但是我们也可以在存储的时候手动设置cookie的过期时间
- 路径限制:存cookie的时候可以指定路径 只允许子路径读取外层cookie 外层不能读取内层的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43<body>
<button id="saveBtn">存</button>
<button id="getBtn">读</button>
<button id="delBtn">删</button>
<script>
// cookie 本地存储
saveBtn.onclick=(()=>{
// 存 cookie 会话cookie 浏览器会话窗口关闭 cookie消失
document.cookie = "username=lmh"
document.cookie = "age=18"
// 存cookie 永久cookie 设置过期时间
var date = new Date()
date.setHours(date.getHours() + 1) // 当前小时+1
document.cookie = `username=lmh;expires=${date.toUTCString()}` // 一小时后过期 因为expires不能解析东八区的时间 所以我们需要给他加上toUTCString方法
})
// 读取cookie
getBtn.onclick=(()=>{
console.log(document.cookie) // username=lmh
getCookie("username")
})
// 处理cookie数据的函数
function getCookie(key){
var str = document.cookie
var arr = str.split(";")
var obj = {}
for(var i=0; i<arr.length; i++){
var subArr = arr[i].split("=")
obj[subArr[0]] = subArr[1]
}
console.log(obj) // {username: 'lmh'}
return obj[key]
}
// 删除cookie
delBtn.onclick=(()=>{
var date = new Date()
date.setHours(date.getHours() - 1) // 当前小时-1
document.cookie = `username=lmh;expires=${date.toUTCString()}`
})
</script>
</body>
25、jsonp
- Jsonp(JSON with Padding) 是json的一种”使用方式” 可以让网页从别的域名(网站)那获取资料 即跨域读取数据
- 为什么我们从不同的域(网站) 访问数据需要一个特殊的技术(JSONP)呢? 这是因为同源策略
25-1、动态创建script标签
1 | const script = document.createElement('script'); |
25-2、解决跨域
- 同源策略:同域名 同端口 同协议
- 不符合同源策略 浏览器为了安全 会阻止请求
- 解决:
- 1、cors 又后端设置 Access-Control-Allow-Origin
- 2、jsonp 前后端必须协作 前后端的函数名必须一致
- jsonp原理:动态创建script标签 src属性指向没有跨域限制 指向一个接口 接口返回的格式一定是***() 函数表达式
- 注意:
- 1、后端接口形式必须是函数名 需要后端配合
- 2、jsonp缺点
(1).onload删除script标签
(2).只能get请求 不能post put delete
1 | <body> |
25-3、小案例–请求百度
1 | <body> |
26、再谈函数
1 | // 函数执行完成之后 会被js垃圾回收机制回收 |
26-1、闭包
- 函数内部返回一个函数 被外界所引用
- 这个函数内部就不会被销毁回收
- 内部函数所用到的外部函数的变量也不会被销毁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 函数内部返回一个函数 被外界所引用
// 这个函数内部就不会被销毁回收
// 内部函数所用到的外部函数的变量也不会被销毁
function outer(){
var name = 'ls' // 因为被内部函数引用 所以不会被回收
var age = 18 // 没有被引用 会被回收
return function(){
return name
}
}
var func = outer()
console.log(func())
/*
优点:让临时变量永驻内存
缺点:内存泄漏
*/
26-2、闭包的应用
记住列表的索引
1 | <body> |
函数防抖
1 | <body> |
27、Sass
- 世界上最成熟、最稳定、最强大的专业级css扩展语言
- sass是一个css的预编译工具
- 也就是能够更优雅的书写css
- sass写出来的东西 浏览器不认识
- 依旧是要转换成css在浏览器中运行
27-1、变量
- 定义一个变量 在后面的代码中使用
- 使用$来定义变量
- 可以对定义的变量使用运算符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16$color:blue;
$width:100px;
$height:100px;
.box{
width: $width;
height: $height;
background: $color;
}
.content{
width: $width*2;
height: $height/2;
background: $color;
}
.footer{
border: 1px solid $color;
} - 转换成css之后
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15.box {
width: 100px;
height: 100px;
background: blue;
}
.content {
width: 200px;
height: 50px;
background: blue;
}
.footer {
border: 1px solid blue;
}
27-2、if分支
- 定义一个变量 在后面的代码中使用
- 使用@if和@else来判断展示什么样式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 定义变量
$isShowTab: true;
$isRed:true;
// 判断
@if($isShowTab==true) {
.box {
position: fixed;
left: 0;
top: 0;
}
}@else {
.box {
position: relative;
}
}
div{
width: 100px;
height: 100px;
@if($isRed==true){
background: red;
}
@else{
background: yellow;
}
} - 转换成css
1
2
3
4
5
6
7
8
9
10
11
12.box {
position: fixed;
left: 0;
top: 0;
}
div {
width: 100px;
height: 100px;
background: red;
}
27-3、for循环
第一种写法
1 | // from 1 to 5 表示1~4 不包括5 |
- 转换成css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23li:nth-child(1) {
position: absolute;
left: 0px;
top: 0px;
}
li:nth-child(2) {
position: absolute;
left: 100px;
top: 100px;
}
li:nth-child(3) {
position: absolute;
left: 200px;
top: 200px;
}
li:nth-child(4) {
position: absolute;
left: 300px;
top: 300px;
}
第二种写法
1 | // from 1 through 5 表示1~5 包括5 |
- 转换成css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29li:nth-child(1) {
position: absolute;
left: 0px;
top: 0px;
}
li:nth-child(2) {
position: absolute;
left: 100px;
top: 100px;
}
li:nth-child(3) {
position: absolute;
left: 200px;
top: 200px;
}
li:nth-child(4) {
position: absolute;
left: 300px;
top: 300px;
}
li:nth-child(5) {
position: absolute;
left: 400px;
top: 400px;
}
第三种写法
1 | $color:red,green,yellow,blue; |
- 转换成css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
li:nth-child(1) {
background: red;
}
li:nth-child(2) {
background: green;
}
li:nth-child(3) {
background: yellow;
}
li:nth-child(4) {
background: blue;
}
27-4、函数混入
基本写法
1
2
3
4
5
6
7
8
9
10
11
12
13// @mixin 表示我这是一段等待被混入的代码
@mixin lmh_mixin{
transition: all 1s;
-webkit-transition: all 1s;
-moz-transition: all 1s;
}
// @include 表示引入一个方法
.box{
@include lmh_mixin
}
.content{
@include lmh_mixin
}转换成css
1
2
3
4
5
6
7
8
9
10
11.box {
transition: all 1s;
-webkit-transition: all 1s;
-moz-transition: all 1s;
}
.content {
transition: all 1s;
-webkit-transition: all 1s;
-moz-transition: all 1s;
}传参写法
1
2
3
4
5
6
7
8
9
10
11
12
13// @mixin 表示我这是一段等待被混入的代码
@mixin lmh_mixin($a,$b){
transition: $a $b;
-webkit-transition: $a $b;
-moz-transition: $a $b;
}
// @include 表示引入一个方法
.box{
@include lmh_mixin(all,1s)
}
.content{
@include lmh_mixin(width,2s)
}转换成css
1
2
3
4
5
6
7
8
9
10
11
12.box {
transition: all 1s;
-webkit-transition: all 1s;
-moz-transition: all 1s;
}
.content {
transition: width 2s;
-webkit-transition: width 2s;
-moz-transition: width 2s;
}
27-5、嵌套
1 | div { |
- 转换成css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26div {
width: 100px;
height: 100px;
}
div p {
width: 50px;
height: 50px;
}
div p span {
color: red;
}
ul > li {
background-color: yellow;
}
ul > li:hover {
background-color: red;
}
ul > li.active {
background-color: blue;
}
27-6、继承
1 | .base{ |
- 转换成css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15.base, .btn, .btn1, .btn2 {
width: 100px;
height: 100px;
outline: none;
}
.btn {
background-color: green;
}
.btn1 {
background-color: red;
}
.btn2 {
background-color: blue;
}